From 48d91fd889f7286f7b0099adb2eaeb8056eed5b4 Mon Sep 17 00:00:00 2001 From: kib Date: Fri, 7 Dec 2018 15:17:29 +0000 Subject: [PATCH] Add new file handle system calls. Namely, getfhat(2), fhlink(2), fhlinkat(2), fhreadlink(2). The syscalls are provided for a NFS userspace server (nfs-ganesha). Submitted by: Jack Halford Sponsored by: Gandi.net Tested by: pho Feedback from: brooks, markj MFC after: 1 week Differential revision: https://reviews.freebsd.org/D18359 --- lib/libc/sys/Makefile.inc | 5 +- lib/libc/sys/Symbol.map | 7 + lib/libc/sys/fhlink.2 | 268 +++++++++++++++++++++++++++ lib/libc/sys/fhreadlink.2 | 92 +++++++++ lib/libc/sys/getfh.2 | 126 ++++++++++++- sys/compat/freebsd32/syscalls.master | 7 + sys/kern/syscalls.master | 28 +++ sys/kern/vfs_syscalls.c | 227 ++++++++++++++++++----- sys/sys/mount.h | 4 + 9 files changed, 716 insertions(+), 48 deletions(-) create mode 100644 lib/libc/sys/fhlink.2 create mode 100644 lib/libc/sys/fhreadlink.2 diff --git a/lib/libc/sys/Makefile.inc b/lib/libc/sys/Makefile.inc index 31b0af4b7d82..bc8299eea8f2 100644 --- a/lib/libc/sys/Makefile.inc +++ b/lib/libc/sys/Makefile.inc @@ -184,7 +184,9 @@ MAN+= abort2.2 \ extattr_get_file.2 \ fcntl.2 \ ffclock.2 \ + fhlink.2 \ fhopen.2 \ + fhreadlink.2 \ flock.2 \ fork.2 \ fsync.2 \ @@ -395,7 +397,8 @@ MLINKS+=ffclock.2 ffclock_getcounter.2 \ MLINKS+=fhopen.2 fhstat.2 fhopen.2 fhstatfs.2 MLINKS+=fsync.2 fdatasync.2 MLINKS+=getdirentries.2 getdents.2 -MLINKS+=getfh.2 lgetfh.2 +MLINKS+=getfh.2 lgetfh.2 \ + getfh.2 getfhat.2 MLINKS+=getgid.2 getegid.2 MLINKS+=getitimer.2 setitimer.2 MLINKS+=getlogin.2 getlogin_r.3 diff --git a/lib/libc/sys/Symbol.map b/lib/libc/sys/Symbol.map index e2acb9344580..a48c693575f8 100644 --- a/lib/libc/sys/Symbol.map +++ b/lib/libc/sys/Symbol.map @@ -401,6 +401,13 @@ FBSD_1.5 { cpuset_setdomain; }; +FBSD_1.6 { + fhlink; + fhlinkat; + fhreadlink; + getfhat; +}; + FBSDprivate_1.0 { ___acl_aclcheck_fd; __sys___acl_aclcheck_fd; diff --git a/lib/libc/sys/fhlink.2 b/lib/libc/sys/fhlink.2 new file mode 100644 index 000000000000..d15ffb628ce8 --- /dev/null +++ b/lib/libc/sys/fhlink.2 @@ -0,0 +1,268 @@ +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.\" Copyright (c) 2018 Gandi +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd November 29, 2018 +.Dt FHLINK 2 +.Os +.Sh NAME +.Nm fhlink , +.Nm fhlinkat +.Nd make a hard file link +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In unistd.h +.Ft int +.Fn fhlink "fhandle_t *fhp" "const char *to" +.Ft int +.Fn fhlinkat "fhandle_t *fhp" "int tofd" "const char *to" +.Fc +.Sh DESCRIPTION +The +.Fn fhlink +system call +atomically creates the specified directory entry (hard link) +.Fa to +with the attributes of the underlying object pointed at by +.Fa fhp . +If the link is successful: the link count of the underlying object +is incremented; +.Fa fhp +and +.Fa to +share equal access and rights +to the +underlying object. +.Pp +If +.Fa fhp +is removed, the file +.Fa to +is not deleted and the link count of the +underlying object is +decremented. +.Pp +The object pointed at by the +.Fa fhp +argument +must exist for the hard link to +succeed and +both +.Fa fhp +and +.Fa to +must be in the same file system. +The +.Fa fhp +argument +may not be a directory. +.Pp +The +.Fn fhlinkat +system call is equivalent to +.Fa fhlink +except in the case where +.Fa to +is a relative paths. +In this case a relative path +.Fa to +is interpreted relative to +the directory associated with the file descriptor +.Fa tofd +instead of the current working directory. +.Pp +Values for +.Fa flag +are constructed by a bitwise-inclusive OR of flags from the following +list, defined in +.In fcntl.h : +.Bl -tag -width indent +.It Dv AT_SYMLINK_FOLLOW +If +.Fa fhp +names a symbolic link, a new link for the target of the symbolic link is +created. +.It Dv AT_BENEATH +Only allow to link to a file which is beneath of the topping directory. +See the description of the +.Dv O_BENEATH +flag in the +.Xr open 2 +manual page. +.El +.Pp +If +.Fn fhlinkat +is passed the special value +.Dv AT_FDCWD +in the +.Fa tofd +parameter, the current working directory is used for the +.Fa to +argument. +If +.Fa tofd +has value +.Dv AT_FDCWD , +the behavior is identical to a call to +.Fn link . +Unless +.Fa flag +contains the +.Dv AT_SYMLINK_FOLLOW +flag, if +.Fa fhp +names a symbolic link, a new link is created for the symbolic link +.Fa fhp +and not its target. +.Sh RETURN VALUES +.Rv -std link +.Sh ERRORS +The +.Fn fhlink +system call +will fail and no link will be created if: +.Bl -tag -width Er +.It Bq Er ENOTDIR +A component of +.Fa to +prefix is not a directory. +.It Bq Er ENAMETOOLONG +A component of +.Fa to +exceeded 255 characters, +or entire length of +.Fa to +name exceeded 1023 characters. +.It Bq Er ENOENT +A component of +.Fa to +prefix does not exist. +.It Bq Er EOPNOTSUPP +The file system containing the file pointed at by +.Fa fhp +does not support links. +.It Bq Er EMLINK +The link count of the file pointed at by +.Fa fhp +would exceed 32767. +.It Bq Er EACCES +A component of +.Fa to +prefix denies search permission. +.It Bq Er EACCES +The requested link requires writing in a directory with a mode +that denies write permission. +.It Bq Er ELOOP +Too many symbolic links were encountered in translating one of the pathnames. +.It Bq Er ENOENT +The file pointed at by +.Fa fhp +does not exist. +.It Bq Er EEXIST +The link named by +.Fa to +does exist. +.It Bq Er EPERM +The file pointed at by +.Fa fhp +is a directory. +.It Bq Er EPERM +The file pointed at by +.Fa fhp +has its immutable or append-only flag set, see the +.Xr chflags 2 +manual page for more information. +.It Bq Er EPERM +The parent directory of the file named by +.Fa to +has its immutable flag set. +.It Bq Er EXDEV +The link named by +.Fa to +and the file pointed at by +.Fa fhp +are on different file systems. +.It Bq Er ENOSPC +The directory in which the entry for the new link is being placed +cannot be extended because there is no space left on the file +system containing the directory. +.It Bq Er EDQUOT +The directory in which the entry for the new link +is being placed cannot be extended because the +user's quota of disk blocks on the file system +containing the directory has been exhausted. +.It Bq Er EIO +An I/O error occurred while reading from or writing to +the file system to make the directory entry. +.It Bq Er EROFS +The requested link requires writing in a directory on a read-only file +system. +.It Bq Er EFAULT +One of the pathnames specified +is outside the process's allocated address space. +.It Bq Er ESTALE +The file handle +.Fa fhp +is no longer valid +.El +.Pp +In addition to the errors returned by the +.Fn fhlink , +the +.Fn fhlinkat +system call may fail if: +.Bl -tag -width Er +.It Bq Er EBADF +The +.Fa fhp +or +.Fa to +argument does not specify an absolute path and the +.Fa tofd +argument, is not +.Dv AT_FDCWD +nor a valid file descriptor open for searching. +.It Bq Er EINVAL +The value of the +.Fa flag +argument is not valid. +.It Bq Er ENOTDIR +The +.Fa fhp +or +.Fa to +argument is not an absolute path and +.Fa tofd +is not +.Dv AT_FDCWD +nor a file descriptor associated with a directory. +.El +.Sh SEE ALSO +.Xr fhstat 2 , +.Xr fhreadlink 2 , +.Xr fhopen 2 , diff --git a/lib/libc/sys/fhreadlink.2 b/lib/libc/sys/fhreadlink.2 new file mode 100644 index 000000000000..599db0985eb3 --- /dev/null +++ b/lib/libc/sys/fhreadlink.2 @@ -0,0 +1,92 @@ +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.\" Copyright (c) 2018 Gandi +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd November 29, 2018 +.Dt FHREADLINK 2 +.Os +.Sh NAME +.Nm fhreadlink +.Nd read value of a symbolic link +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/param.h +.In sys/mount.h +.Ft int +.Fn fhreadlink "fhandle_t *fhp" "char *buf" "size_t bufsize" +.Fc +.Sh DESCRIPTION +The +.Fn fhreadlink +system call +places the contents of the symbolic link +.Fa fhp +in the buffer +.Fa buf , +which has size +.Fa bufsiz . +The +.Fn fhreadlink +system call does not append a +.Dv NUL +character to +.Fa buf . +.Pp +.Sh RETURN VALUES +The call returns the count of characters placed in the buffer +if it succeeds, or a \-1 if an error occurs, placing the error +code in the global variable +.Va errno . +.Sh ERRORS +The +.Fn readlink +system call +will fail if: +.Bl -tag -width Er +.It Bq Er ENOENT +The named file does not exist. +.It Bq Er ELOOP +Too many symbolic links were encountered in translating the file handle +.Fa fhp . +.It Bq Er EINVAL +The named file is not a symbolic link. +.It Bq Er EIO +An I/O error occurred while reading from the file system. +.It Bq Er EFAULT +The +.Fa buf +argument +extends outside the process's allocated address space. +.It Bq Er ESTALE +The file handle +.Fa fhp +is no longer valid +.El +.El +.Sh SEE ALSO +.Xr fhstat 2 , +.Xr fhlink 2 , diff --git a/lib/libc/sys/getfh.2 b/lib/libc/sys/getfh.2 index 81e6c6a27396..1877a37a02bf 100644 --- a/lib/libc/sys/getfh.2 +++ b/lib/libc/sys/getfh.2 @@ -1,5 +1,6 @@ .\" Copyright (c) 1989, 1991, 1993 .\" The Regents of the University of California. All rights reserved. +.\" Copyright (c) 2018 Gandi .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -28,12 +29,13 @@ .\" @(#)getfh.2 8.1 (Berkeley) 6/9/93 .\" $FreeBSD$ .\" -.Dd April 14, 2011 +.Dd December 7, 2018 .Dt GETFH 2 .Os .Sh NAME .Nm getfh , -.Nm lgetfh +.Nm lgetfh , +.Nm getfhat .Nd get file handle .Sh LIBRARY .Lb libc @@ -44,6 +46,8 @@ .Fn getfh "const char *path" "fhandle_t *fhp" .Ft int .Fn lgetfh "const char *path" "fhandle_t *fhp" +.Ft int +.Fn getfhat "int fd" "const char *path" "fhandle_t *fhp" "int flag" .Sh DESCRIPTION The .Fn getfh @@ -51,6 +55,7 @@ system call returns a file handle for the specified file or directory in the file handle pointed to by .Fa fhp . +.Pp The .Fn lgetfh system call is like @@ -62,6 +67,85 @@ returns information about the link, while .Fn getfh returns information about the file the link references. +.Pp +The +.Fn getfhat +system call is equivalent to +.Fn getfh +and +.Fn lgetfh +except when the +.Fa path +specifies a relative or NULL path, or the +.Dv AT_BENEATH +flag is provided. +For +.Fn getfhat +and relative or NULL +.Fa path , +the status is retrieved from a file relative to +the directory associated with the file descriptor +.Fa fd +instead of the current working directory. +For +.Dv AT_BENEATH +and absolute +.Fa path , +the status is retrieved from a file specified by the +.Fa path , +but additional permission checks are performed, see below. +.Pp +The values for the +.Fa flag +are constructed by a bitwise-inclusive OR of flags from this list, +defined in +.In fcntl.h : +.Bl -tag -width indent +.It Dv AT_SYMLINK_NOFOLLOW +If +.Fa path +names a symbolic link, the status of the symbolic link is returned. +.It Dv AT_BENEATH +Only stat files and directories below the topping directory. +See the description of the +.Dv O_BENEATH +flag in the +.Xr open 2 +manual page. +.El +.Pp +If +.Fn getfhat +is passed the special value +.Dv AT_FDCWD +in the +.Fa fd +parameter, the current working directory is used and the behavior is +identical to a call to +.Fn getfth +or +.Fn lgetfh +respectively, depending on whether or not the +.Dv AT_SYMLINK_NOFOLLOW +bit is set in +.Fa flag . +.Pp +When +.Fn getfhat +is called with an absolute +.Fa path +without the +.Dv AT_BENEATH +flag, it ignores the +.Fa fd +argument. +When +.Dv AT_BENEATH +is specified with an absolute +.Fa path , +a directory passed by the +.Fa fd +argument is used as the topping point for the resolution. These system calls are restricted to the superuser. .Sh RETURN VALUES .Rv -std @@ -99,11 +183,49 @@ The .Fa fhp argument points to an invalid address. +.It Bq Er EFAULT +The +.Fa path +argument +points to an invalid address. .It Bq Er EIO An .Tn I/O error occurred while reading from or writing to the file system. +.It Bq Er ESTALE +The file handle +.Fa fhp +is no longer valid. .El +.Pp +In addition to the errors returned by +.Fn getfh , +and +.Fn lgetfh , +the +.Fn getfhat +system call may fail if: +.Bl -tag -width Er +.It Bq Er EBADF +The +.Fa path +argument does not specify an absolute path and the +.Fa fd +argument, is neither +.Dv AT_FDCWD +nor a valid file descriptor open for searching. +.It Bq Er EINVAL +The value of the +.Fa flag +argument is not valid. +.It Bq Er ENOTDIR +The +.Fa path +argument is not an absolute path and +.Fa fd +is neither +.Dv AT_FDCWD +nor a file descriptor associated with a directory. .Sh SEE ALSO .Xr fhopen 2 , .Xr open 2 , diff --git a/sys/compat/freebsd32/syscalls.master b/sys/compat/freebsd32/syscalls.master index c7d53ff7675d..567e5677d412 100644 --- a/sys/compat/freebsd32/syscalls.master +++ b/sys/compat/freebsd32/syscalls.master @@ -1138,5 +1138,12 @@ int policy); } 563 AUE_NULL NOPROTO { int getrandom(void *buf, size_t buflen, \ unsigned int flags); } +564 AUE_NULL NOPROTO { int getfhat( int fd, char *path, \ + struct fhandle *fhp, int flags); } +565 AUE_NULL NOPROTO { int fhlink( struct fhandle *fhp, const char *to ); } +566 AUE_NULL NOPROTO { int fhlinkat( struct fhandle *fhp, int tofd, \ + const char *to); } +567 AUE_NULL NOPROTO { int fhreadlink( struct fhandle *fhp, char *buf, \ + size_t bufsize); } ; vim: syntax=off diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index 6e0d289f04c0..03897946b475 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -3139,6 +3139,34 @@ unsigned int flags ); } +564 AUE_NULL STD { + int getfhat( + int fd, + _In_z_ char *path, + _Out_ struct fhandle *fhp, + int flags + ); + } +565 AUE_NULL STD { + int fhlink( + _In_ struct fhandle *fhp, + _In_z_ const char *to + ); + } +566 AUE_NULL STD { + int fhlinkat( + _In_ struct fhandle *fhp, + int tofd, + _In_z_ const char *to, + ); + } +567 AUE_NULL STD { + int fhreadlink( + _In_ struct fhandle *fhp, + _Out_writes_(bufsize) char *buf, + size_t bufsize + ); + } ; Please copy any additions and changes to the following compatability tables: ; sys/compat/freebsd32/syscalls.master diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index 24efef9d6790..6e8d2bfa7329 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -105,6 +105,14 @@ static int setutimes(struct thread *td, struct vnode *, const struct timespec *, int, int); static int vn_access(struct vnode *vp, int user_flags, struct ucred *cred, struct thread *td); +static int kern_fhlinkat(struct thread *td, int fd, const char *path, + enum uio_seg pathseg, fhandle_t *fhp); +static int kern_getfhat(struct thread *td, int flags, int fd, + const char *path, enum uio_seg pathseg, fhandle_t *fhp); +static int kern_readlink_vp(struct vnode *vp, char *buf, enum uio_seg bufseg, + size_t count, struct thread *td); +static int kern_linkat_vp(struct thread *td, struct vnode *vp, int fd, + const char *path, enum uio_seg segflag); /* * Sync each mounted filesystem. @@ -1492,28 +1500,38 @@ can_hardlink(struct vnode *vp, struct ucred *cred) int kern_linkat(struct thread *td, int fd1, int fd2, const char *path1, - const char *path2, enum uio_seg segflg, int follow) + const char *path2, enum uio_seg segflag, int follow) { struct vnode *vp; - struct mount *mp; struct nameidata nd; int error; -again: - bwillwrite(); - NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, segflg, path1, fd1, - &cap_linkat_source_rights, td); + do { + bwillwrite(); + NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, segflag, path1, fd1, + &cap_linkat_source_rights, td); + if ((error = namei(&nd)) != 0) + return (error); + NDFREE(&nd, NDF_ONLY_PNBUF); + vp = nd.ni_vp; + } while ((error = kern_linkat_vp(td, vp, fd2, path2, segflag) == EAGAIN)); + return (error); +} + +static int +kern_linkat_vp(struct thread *td, struct vnode *vp, int fd, const char *path, + enum uio_seg segflag) +{ + struct nameidata nd; + struct mount *mp; + int error; - if ((error = namei(&nd)) != 0) - return (error); - NDFREE(&nd, NDF_ONLY_PNBUF); - vp = nd.ni_vp; if (vp->v_type == VDIR) { vrele(vp); return (EPERM); /* POSIX */ } NDINIT_ATRIGHTS(&nd, CREATE, - LOCKPARENT | SAVENAME | AUDITVNODE2 | NOCACHE, segflg, path2, fd2, + LOCKPARENT | SAVENAME | AUDITVNODE2 | NOCACHE, segflag, path, fd, &cap_linkat_target_rights, td); if ((error = namei(&nd)) == 0) { if (nd.ni_vp != NULL) { @@ -1557,7 +1575,7 @@ kern_linkat(struct thread *td, int fd1, int fd2, const char *path1, V_XSLEEP | PCATCH); if (error != 0) return (error); - goto again; + return (EAGAIN); } error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); VOP_UNLOCK(vp, 0); @@ -1568,7 +1586,7 @@ kern_linkat(struct thread *td, int fd1, int fd2, const char *path1, vput(nd.ni_dvp); NDFREE(&nd, NDF_ONLY_PNBUF); vrele(vp); - goto again; + return (EAGAIN); } } vrele(vp); @@ -2484,8 +2502,6 @@ kern_readlinkat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, char *buf, enum uio_seg bufseg, size_t count) { struct vnode *vp; - struct iovec aiov; - struct uio auio; struct nameidata nd; int error; @@ -2499,12 +2515,29 @@ kern_readlinkat(struct thread *td, int fd, const char *path, return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; + + error = kern_readlink_vp(vp, buf, bufseg, count, td); + vput(vp); + + return (error); +} + +/* + * Helper function to readlink from a vnode + */ +static int +kern_readlink_vp(struct vnode *vp, char *buf, enum uio_seg bufseg, size_t count, + struct thread *td) +{ + struct iovec aiov; + struct uio auio; + int error; + + ASSERT_VOP_LOCKED(vp, "kern_readlink_vp(): vp not locked"); #ifdef MAC error = mac_vnode_check_readlink(td->td_ucred, vp); - if (error != 0) { - vput(vp); + if (error != 0) return (error); - } #endif if (vp->v_type != VLNK && (vp->v_vflag & VV_READLINK) == 0) error = EINVAL; @@ -2521,7 +2554,6 @@ kern_readlinkat(struct thread *td, int fd, const char *path, error = VOP_READLINK(vp, &auio, td->td_ucred); td->td_retval[0] = count - auio.uio_resid; } - vput(vp); return (error); } @@ -4119,45 +4151,60 @@ getvnode(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp) */ #ifndef _SYS_SYSPROTO_H_ struct lgetfh_args { - char *fname; + char *fname; fhandle_t *fhp; }; #endif int sys_lgetfh(struct thread *td, struct lgetfh_args *uap) { - struct nameidata nd; - fhandle_t fh; - struct vnode *vp; - int error; - error = priv_check(td, PRIV_VFS_GETFH); - if (error != 0) - return (error); - NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | AUDITVNODE1, UIO_USERSPACE, - uap->fname, td); - error = namei(&nd); - if (error != 0) - return (error); - NDFREE(&nd, NDF_ONLY_PNBUF); - vp = nd.ni_vp; - bzero(&fh, sizeof(fh)); - fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; - error = VOP_VPTOFH(vp, &fh.fh_fid); - vput(vp); - if (error == 0) - error = copyout(&fh, uap->fhp, sizeof (fh)); - return (error); + return (kern_getfhat(td, AT_SYMLINK_NOFOLLOW, AT_FDCWD, uap->fname, + UIO_USERSPACE, uap->fhp)); } #ifndef _SYS_SYSPROTO_H_ struct getfh_args { - char *fname; + char *fname; fhandle_t *fhp; }; #endif int sys_getfh(struct thread *td, struct getfh_args *uap) +{ + + return (kern_getfhat(td, 0, AT_FDCWD, uap->fname, UIO_USERSPACE, + uap->fhp)); +} + +/* + * syscall for the rpc.lockd to use to translate an open descriptor into + * a NFS file handle. + * + * warning: do not remove the priv_check() call or this becomes one giant + * security hole. + */ +#ifndef _SYS_SYSPROTO_H_ +struct getfhat_args { + int fd; + char *path; + fhandle_t *fhp; + int flags; +}; +#endif +int +sys_getfhat(struct thread *td, struct getfhat_args *uap) +{ + + if ((uap->flags & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) + return (EINVAL); + return (kern_getfhat(td, uap->flags, uap->fd, uap->path ? uap->path : ".", + UIO_USERSPACE, uap->fhp)); +} + +static int +kern_getfhat(struct thread *td, int flags, int fd, const char *path, + enum uio_seg pathseg, fhandle_t *fhp) { struct nameidata nd; fhandle_t fh; @@ -4167,8 +4214,9 @@ sys_getfh(struct thread *td, struct getfh_args *uap) error = priv_check(td, PRIV_VFS_GETFH); if (error != 0) return (error); - NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1, UIO_USERSPACE, - uap->fname, td); + NDINIT_AT(&nd, LOOKUP, ((flags & AT_SYMLINK_NOFOLLOW) != 0 ? NOFOLLOW : + FOLLOW) | ((flags & AT_BENEATH) != 0 ? BENEATH : 0) | LOCKLEAF | + AUDITVNODE1, pathseg, path, fd, td); error = namei(&nd); if (error != 0) return (error); @@ -4179,7 +4227,96 @@ sys_getfh(struct thread *td, struct getfh_args *uap) error = VOP_VPTOFH(vp, &fh.fh_fid); vput(vp); if (error == 0) - error = copyout(&fh, uap->fhp, sizeof (fh)); + error = copyout(&fh, fhp, sizeof (fh)); + return (error); +} + +#ifndef _SYS_SYSPROTO_H_ +struct fhlink_args { + fhandle_t *fhp; + const char *to; +}; +#endif +int +sys_fhlink(struct thread *td, struct fhlink_args *uap) +{ + + return (kern_fhlinkat(td, AT_FDCWD, uap->to, UIO_USERSPACE, uap->fhp)); +} + +#ifndef _SYS_SYSPROTO_H_ +struct fhlinkat_args { + fhandle_t *fhp; + int tofd; + const char *to; +}; +#endif +int +sys_fhlinkat(struct thread *td, struct fhlinkat_args *uap) +{ + + return (kern_fhlinkat(td, uap->tofd, uap->to, UIO_USERSPACE, uap->fhp)); +} + +static int +kern_fhlinkat(struct thread *td, int fd, const char *path, + enum uio_seg pathseg, fhandle_t *fhp) +{ + fhandle_t fh; + struct mount *mp; + struct vnode *vp; + int error; + + error = priv_check(td, PRIV_VFS_GETFH); + if (error != 0) + return (error); + error = copyin(fhp, &fh, sizeof(fh)); + if (error != 0) + return (error); + do { + bwillwrite(); + if ((mp = vfs_busyfs(&fh.fh_fsid)) == NULL) + return (ESTALE); + error = VFS_FHTOVP(mp, &fh.fh_fid, LK_SHARED, &vp); + vfs_unbusy(mp); + if (error != 0) + return (error); + VOP_UNLOCK(vp, 0); + } while ((error = kern_linkat_vp(td, vp, fd, path, pathseg)) == EAGAIN); + return (error); +} + +#ifndef _SYS_SYSPROTO_H_ +struct fhreadlink_args { + fhandle_t *fhp; + char *buf; + size_t bufsize; +}; +#endif +int +sys_fhreadlink(struct thread *td, struct fhreadlink_args *uap) +{ + fhandle_t fh; + struct mount *mp; + struct vnode *vp; + int error; + + error = priv_check(td, PRIV_VFS_GETFH); + if (error != 0) + return (error); + if (uap->bufsize > IOSIZE_MAX) + return (EINVAL); + error = copyin(uap->fhp, &fh, sizeof(fh)); + if (error != 0) + return (error); + if ((mp = vfs_busyfs(&fh.fh_fsid)) == NULL) + return (ESTALE); + error = VFS_FHTOVP(mp, &fh.fh_fid, LK_SHARED, &vp); + vfs_unbusy(mp); + if (error != 0) + return (error); + error = kern_readlink_vp(vp, uap->buf, UIO_USERSPACE, uap->bufsize, td); + vput(vp); return (error); } diff --git a/sys/sys/mount.h b/sys/sys/mount.h index ac1c215b9a83..2a5d4cff2a8b 100644 --- a/sys/sys/mount.h +++ b/sys/sys/mount.h @@ -932,11 +932,15 @@ void syncer_resume(void); struct stat; __BEGIN_DECLS +int fhlink(struct fhandle *, const char *); +int fhlinkat(struct fhandle *, int, const char *); int fhopen(const struct fhandle *, int); +int fhreadlink(struct fhandle *, char *, size_t); int fhstat(const struct fhandle *, struct stat *); int fhstatfs(const struct fhandle *, struct statfs *); int fstatfs(int, struct statfs *); int getfh(const char *, fhandle_t *); +int getfhat(int, char *, struct fhandle *, int); int getfsstat(struct statfs *, long, int); int getmntinfo(struct statfs **, int); int lgetfh(const char *, fhandle_t *);