Changeset View
Changeset View
Standalone View
Standalone View
head/sys/kern/vfs_syscalls.c
Show First 20 Lines • Show All 99 Lines • ▼ Show 20 Lines | |||||
static int setfflags(struct thread *td, struct vnode *, u_long); | static int setfflags(struct thread *td, struct vnode *, u_long); | ||||
static int getutimes(const struct timeval *, enum uio_seg, struct timespec *); | static int getutimes(const struct timeval *, enum uio_seg, struct timespec *); | ||||
static int getutimens(const struct timespec *, enum uio_seg, | static int getutimens(const struct timespec *, enum uio_seg, | ||||
struct timespec *, int *); | struct timespec *, int *); | ||||
static int setutimes(struct thread *td, struct vnode *, | static int setutimes(struct thread *td, struct vnode *, | ||||
const struct timespec *, int, int); | const struct timespec *, int, int); | ||||
static int vn_access(struct vnode *vp, int user_flags, struct ucred *cred, | static int vn_access(struct vnode *vp, int user_flags, struct ucred *cred, | ||||
struct thread *td); | 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. | * Sync each mounted filesystem. | ||||
*/ | */ | ||||
#ifndef _SYS_SYSPROTO_H_ | #ifndef _SYS_SYSPROTO_H_ | ||||
struct sync_args { | struct sync_args { | ||||
int dummy; | int dummy; | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 1,371 Lines • ▼ Show 20 Lines | if (error != 0) | ||||
return (error); | return (error); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
int | int | ||||
kern_linkat(struct thread *td, int fd1, int fd2, const char *path1, | 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 vnode *vp; | ||||
struct mount *mp; | |||||
struct nameidata nd; | struct nameidata nd; | ||||
int error; | int error; | ||||
again: | do { | ||||
bwillwrite(); | bwillwrite(); | ||||
NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, segflg, path1, fd1, | NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, segflag, path1, fd1, | ||||
&cap_linkat_source_rights, td); | &cap_linkat_source_rights, td); | ||||
if ((error = namei(&nd)) != 0) | if ((error = namei(&nd)) != 0) | ||||
return (error); | return (error); | ||||
NDFREE(&nd, NDF_ONLY_PNBUF); | NDFREE(&nd, NDF_ONLY_PNBUF); | ||||
vp = nd.ni_vp; | 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 (vp->v_type == VDIR) { | if (vp->v_type == VDIR) { | ||||
vrele(vp); | vrele(vp); | ||||
return (EPERM); /* POSIX */ | return (EPERM); /* POSIX */ | ||||
} | } | ||||
NDINIT_ATRIGHTS(&nd, CREATE, | NDINIT_ATRIGHTS(&nd, CREATE, | ||||
LOCKPARENT | SAVENAME | AUDITVNODE2 | NOCACHE, segflg, path2, fd2, | LOCKPARENT | SAVENAME | AUDITVNODE2 | NOCACHE, segflag, path, fd, | ||||
&cap_linkat_target_rights, td); | &cap_linkat_target_rights, td); | ||||
if ((error = namei(&nd)) == 0) { | if ((error = namei(&nd)) == 0) { | ||||
if (nd.ni_vp != NULL) { | if (nd.ni_vp != NULL) { | ||||
NDFREE(&nd, NDF_ONLY_PNBUF); | NDFREE(&nd, NDF_ONLY_PNBUF); | ||||
if (nd.ni_dvp == nd.ni_vp) | if (nd.ni_dvp == nd.ni_vp) | ||||
vrele(nd.ni_dvp); | vrele(nd.ni_dvp); | ||||
else | else | ||||
vput(nd.ni_dvp); | vput(nd.ni_dvp); | ||||
Show All 27 Lines | #endif | ||||
if (error != 0) { | if (error != 0) { | ||||
vput(vp); | vput(vp); | ||||
vput(nd.ni_dvp); | vput(nd.ni_dvp); | ||||
NDFREE(&nd, NDF_ONLY_PNBUF); | NDFREE(&nd, NDF_ONLY_PNBUF); | ||||
error = vn_start_write(NULL, &mp, | error = vn_start_write(NULL, &mp, | ||||
V_XSLEEP | PCATCH); | V_XSLEEP | PCATCH); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
goto again; | return (EAGAIN); | ||||
} | } | ||||
error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); | error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); | ||||
VOP_UNLOCK(vp, 0); | VOP_UNLOCK(vp, 0); | ||||
vput(nd.ni_dvp); | vput(nd.ni_dvp); | ||||
vn_finished_write(mp); | vn_finished_write(mp); | ||||
NDFREE(&nd, NDF_ONLY_PNBUF); | NDFREE(&nd, NDF_ONLY_PNBUF); | ||||
} else { | } else { | ||||
vput(nd.ni_dvp); | vput(nd.ni_dvp); | ||||
NDFREE(&nd, NDF_ONLY_PNBUF); | NDFREE(&nd, NDF_ONLY_PNBUF); | ||||
vrele(vp); | vrele(vp); | ||||
goto again; | return (EAGAIN); | ||||
} | } | ||||
} | } | ||||
vrele(vp); | vrele(vp); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Make a symbolic link. | * Make a symbolic link. | ||||
▲ Show 20 Lines • Show All 899 Lines • ▼ Show 20 Lines | return (kern_readlinkat(td, uap->fd, uap->path, UIO_USERSPACE, | ||||
uap->buf, UIO_USERSPACE, uap->bufsize)); | uap->buf, UIO_USERSPACE, uap->bufsize)); | ||||
} | } | ||||
int | int | ||||
kern_readlinkat(struct thread *td, int fd, const char *path, | kern_readlinkat(struct thread *td, int fd, const char *path, | ||||
enum uio_seg pathseg, char *buf, enum uio_seg bufseg, size_t count) | enum uio_seg pathseg, char *buf, enum uio_seg bufseg, size_t count) | ||||
{ | { | ||||
struct vnode *vp; | struct vnode *vp; | ||||
struct iovec aiov; | |||||
struct uio auio; | |||||
struct nameidata nd; | struct nameidata nd; | ||||
int error; | int error; | ||||
if (count > IOSIZE_MAX) | if (count > IOSIZE_MAX) | ||||
return (EINVAL); | return (EINVAL); | ||||
NDINIT_AT(&nd, LOOKUP, NOFOLLOW | LOCKSHARED | LOCKLEAF | AUDITVNODE1, | NDINIT_AT(&nd, LOOKUP, NOFOLLOW | LOCKSHARED | LOCKLEAF | AUDITVNODE1, | ||||
pathseg, path, fd, td); | pathseg, path, fd, td); | ||||
if ((error = namei(&nd)) != 0) | if ((error = namei(&nd)) != 0) | ||||
return (error); | return (error); | ||||
NDFREE(&nd, NDF_ONLY_PNBUF); | NDFREE(&nd, NDF_ONLY_PNBUF); | ||||
vp = nd.ni_vp; | vp = nd.ni_vp; | ||||
#ifdef MAC | |||||
error = mac_vnode_check_readlink(td->td_ucred, vp); | error = kern_readlink_vp(vp, buf, bufseg, count, td); | ||||
if (error != 0) { | |||||
vput(vp); | vput(vp); | ||||
return (error); | 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) | |||||
return (error); | |||||
#endif | #endif | ||||
if (vp->v_type != VLNK && (vp->v_vflag & VV_READLINK) == 0) | if (vp->v_type != VLNK && (vp->v_vflag & VV_READLINK) == 0) | ||||
error = EINVAL; | error = EINVAL; | ||||
else { | else { | ||||
aiov.iov_base = buf; | aiov.iov_base = buf; | ||||
aiov.iov_len = count; | aiov.iov_len = count; | ||||
auio.uio_iov = &aiov; | auio.uio_iov = &aiov; | ||||
auio.uio_iovcnt = 1; | auio.uio_iovcnt = 1; | ||||
auio.uio_offset = 0; | auio.uio_offset = 0; | ||||
auio.uio_rw = UIO_READ; | auio.uio_rw = UIO_READ; | ||||
auio.uio_segflg = bufseg; | auio.uio_segflg = bufseg; | ||||
auio.uio_td = td; | auio.uio_td = td; | ||||
auio.uio_resid = count; | auio.uio_resid = count; | ||||
error = VOP_READLINK(vp, &auio, td->td_ucred); | error = VOP_READLINK(vp, &auio, td->td_ucred); | ||||
td->td_retval[0] = count - auio.uio_resid; | td->td_retval[0] = count - auio.uio_resid; | ||||
} | } | ||||
vput(vp); | |||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Common implementation code for chflags() and fchflags(). | * Common implementation code for chflags() and fchflags(). | ||||
*/ | */ | ||||
static int | static int | ||||
setfflags(struct thread *td, struct vnode *vp, u_long flags) | setfflags(struct thread *td, struct vnode *vp, u_long flags) | ||||
▲ Show 20 Lines • Show All 1,581 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/* | /* | ||||
* Get an (NFS) file handle. | * Get an (NFS) file handle. | ||||
*/ | */ | ||||
#ifndef _SYS_SYSPROTO_H_ | #ifndef _SYS_SYSPROTO_H_ | ||||
struct lgetfh_args { | struct lgetfh_args { | ||||
char *fname; | char *fname; | ||||
fhandle_t *fhp; | fhandle_t *fhp; | ||||
}; | }; | ||||
#endif | #endif | ||||
int | int | ||||
sys_lgetfh(struct thread *td, struct lgetfh_args *uap) | sys_lgetfh(struct thread *td, struct lgetfh_args *uap) | ||||
{ | { | ||||
return (kern_getfhat(td, AT_SYMLINK_NOFOLLOW, AT_FDCWD, uap->fname, | |||||
UIO_USERSPACE, uap->fhp)); | |||||
} | |||||
#ifndef _SYS_SYSPROTO_H_ | |||||
struct getfh_args { | |||||
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; | struct nameidata nd; | ||||
fhandle_t fh; | fhandle_t fh; | ||||
struct vnode *vp; | struct vnode *vp; | ||||
int error; | int error; | ||||
error = priv_check(td, PRIV_VFS_GETFH); | error = priv_check(td, PRIV_VFS_GETFH); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | AUDITVNODE1, UIO_USERSPACE, | NDINIT_AT(&nd, LOOKUP, ((flags & AT_SYMLINK_NOFOLLOW) != 0 ? NOFOLLOW : | ||||
uap->fname, td); | FOLLOW) | ((flags & AT_BENEATH) != 0 ? BENEATH : 0) | LOCKLEAF | | ||||
AUDITVNODE1, pathseg, path, fd, td); | |||||
error = namei(&nd); | error = namei(&nd); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
NDFREE(&nd, NDF_ONLY_PNBUF); | NDFREE(&nd, NDF_ONLY_PNBUF); | ||||
vp = nd.ni_vp; | vp = nd.ni_vp; | ||||
bzero(&fh, sizeof(fh)); | bzero(&fh, sizeof(fh)); | ||||
fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; | fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; | ||||
error = VOP_VPTOFH(vp, &fh.fh_fid); | error = VOP_VPTOFH(vp, &fh.fh_fid); | ||||
vput(vp); | vput(vp); | ||||
if (error == 0) | if (error == 0) | ||||
error = copyout(&fh, uap->fhp, sizeof (fh)); | error = copyout(&fh, fhp, sizeof (fh)); | ||||
return (error); | return (error); | ||||
} | } | ||||
#ifndef _SYS_SYSPROTO_H_ | #ifndef _SYS_SYSPROTO_H_ | ||||
struct getfh_args { | struct fhlink_args { | ||||
char *fname; | |||||
fhandle_t *fhp; | fhandle_t *fhp; | ||||
const char *to; | |||||
}; | }; | ||||
#endif | #endif | ||||
int | int | ||||
sys_getfh(struct thread *td, struct getfh_args *uap) | sys_fhlink(struct thread *td, struct fhlink_args *uap) | ||||
{ | { | ||||
struct nameidata nd; | |||||
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; | fhandle_t fh; | ||||
struct mount *mp; | |||||
struct vnode *vp; | struct vnode *vp; | ||||
int error; | int error; | ||||
error = priv_check(td, PRIV_VFS_GETFH); | error = priv_check(td, PRIV_VFS_GETFH); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1, UIO_USERSPACE, | error = copyin(fhp, &fh, sizeof(fh)); | ||||
uap->fname, td); | |||||
error = namei(&nd); | |||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
NDFREE(&nd, NDF_ONLY_PNBUF); | do { | ||||
vp = nd.ni_vp; | bwillwrite(); | ||||
bzero(&fh, sizeof(fh)); | if ((mp = vfs_busyfs(&fh.fh_fsid)) == NULL) | ||||
fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; | return (ESTALE); | ||||
error = VOP_VPTOFH(vp, &fh.fh_fid); | 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); | vput(vp); | ||||
if (error == 0) | |||||
error = copyout(&fh, uap->fhp, sizeof (fh)); | |||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* syscall for the rpc.lockd to use to translate a NFS file handle into an | * syscall for the rpc.lockd to use to translate a NFS file handle into an | ||||
* open descriptor. | * open descriptor. | ||||
* | * | ||||
* warning: do not remove the priv_check() call or this becomes one giant | * warning: do not remove the priv_check() call or this becomes one giant | ||||
▲ Show 20 Lines • Show All 428 Lines • Show Last 20 Lines |