Add AT_EMPTY_PATH for several *at(2) syscalls
It is currently allowed to fchownat(2), fchmodat(2), fchflagsat(2), utimensat(2), fstatat(2), and linkat(2). For linkat(2), PRIV_VFS_FHOPEN privilege is required to exercise the flag. It allows to link any open file. Requested by: trasz Tested by: pho, trasz Reviewed by: markj Sponsored by: The FreeBSD Foundation MFC after: 2 weeks Differential revision: https://reviews.freebsd.org/D29111
This commit is contained in:
parent
d51b4b0aac
commit
509124b626
@ -28,7 +28,7 @@
|
|||||||
.\" @(#)access.2 8.2 (Berkeley) 4/1/94
|
.\" @(#)access.2 8.2 (Berkeley) 4/1/94
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd February 23, 2021
|
.Dd March 30, 2021
|
||||||
.Dt ACCESS 2
|
.Dt ACCESS 2
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -129,6 +129,17 @@ See the description of the
|
|||||||
flag in the
|
flag in the
|
||||||
.Xr open 2
|
.Xr open 2
|
||||||
manual page.
|
manual page.
|
||||||
|
.It Dv AT_EMPTY_PATH
|
||||||
|
If the
|
||||||
|
.Fa path
|
||||||
|
argument is an empty string, operate on the file or directory
|
||||||
|
referenced by the descriptor
|
||||||
|
.Fa fd .
|
||||||
|
If
|
||||||
|
.Fa fd
|
||||||
|
is equal to
|
||||||
|
.Dv AT_FDCWD ,
|
||||||
|
operate on the current working directory.
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
Even if a process's real or effective user has appropriate privileges
|
Even if a process's real or effective user has appropriate privileges
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
.\" @(#)chflags.2 8.3 (Berkeley) 5/2/95
|
.\" @(#)chflags.2 8.3 (Berkeley) 5/2/95
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd February 23, 2021
|
.Dd March 30, 2021
|
||||||
.Dt CHFLAGS 2
|
.Dt CHFLAGS 2
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -103,6 +103,17 @@ See the description of the
|
|||||||
flag in the
|
flag in the
|
||||||
.Xr open 2
|
.Xr open 2
|
||||||
manual page.
|
manual page.
|
||||||
|
.It Dv AT_EMPTY_PATH
|
||||||
|
If the
|
||||||
|
.Fa path
|
||||||
|
argument is an empty string, operate on the file or directory
|
||||||
|
referenced by the descriptor
|
||||||
|
.Fa fd .
|
||||||
|
If
|
||||||
|
.Fa fd
|
||||||
|
is equal to
|
||||||
|
.Dv AT_FDCWD ,
|
||||||
|
operate on the current working directory.
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
If
|
If
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
.\" @(#)chmod.2 8.1 (Berkeley) 6/4/93
|
.\" @(#)chmod.2 8.1 (Berkeley) 6/4/93
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd February 23, 2021
|
.Dd March 30, 2021
|
||||||
.Dt CHMOD 2
|
.Dt CHMOD 2
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -110,6 +110,17 @@ See the description of the
|
|||||||
flag in the
|
flag in the
|
||||||
.Xr open 2
|
.Xr open 2
|
||||||
manual page.
|
manual page.
|
||||||
|
.It Dv AT_EMPTY_PATH
|
||||||
|
If the
|
||||||
|
.Fa path
|
||||||
|
argument is an empty string, operate on the file or directory
|
||||||
|
referenced by the descriptor
|
||||||
|
.Fa fd .
|
||||||
|
If
|
||||||
|
.Fa fd
|
||||||
|
is equal to
|
||||||
|
.Dv AT_FDCWD ,
|
||||||
|
operate on the current working directory.
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
If
|
If
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
.\" @(#)chown.2 8.4 (Berkeley) 4/19/94
|
.\" @(#)chown.2 8.4 (Berkeley) 4/19/94
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd February 23, 2021
|
.Dd March 30, 2021
|
||||||
.Dt CHOWN 2
|
.Dt CHOWN 2
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -127,6 +127,17 @@ See the description of the
|
|||||||
flag in the
|
flag in the
|
||||||
.Xr open 2
|
.Xr open 2
|
||||||
manual page.
|
manual page.
|
||||||
|
.It Dv AT_EMPTY_PATH
|
||||||
|
If the
|
||||||
|
.Fa path
|
||||||
|
argument is an empty string, operate on the file or directory
|
||||||
|
referenced by the descriptor
|
||||||
|
.Fa fd .
|
||||||
|
If
|
||||||
|
.Fa fd
|
||||||
|
is equal to
|
||||||
|
.Dv AT_FDCWD ,
|
||||||
|
operate on the current working directory.
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
If
|
If
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
.\" @(#)link.2 8.3 (Berkeley) 1/12/94
|
.\" @(#)link.2 8.3 (Berkeley) 1/12/94
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd February 23, 2021
|
.Dd March 30, 2021
|
||||||
.Dt LINK 2
|
.Dt LINK 2
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -124,6 +124,15 @@ See the description of the
|
|||||||
flag in the
|
flag in the
|
||||||
.Xr open 2
|
.Xr open 2
|
||||||
manual page.
|
manual page.
|
||||||
|
.It Dv AT_EMPTY_PATH
|
||||||
|
If the
|
||||||
|
.Fa path2
|
||||||
|
argument is an empty string, link the file referenced by the descriptor
|
||||||
|
.Fa fd2 .
|
||||||
|
The operation requires that the calling process has the
|
||||||
|
.Dv PRIV_VFS_FHOPEN
|
||||||
|
privilege, effectively being executed with effective user
|
||||||
|
.Dv root .
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
If
|
If
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
.\" @(#)stat.2 8.4 (Berkeley) 5/1/95
|
.\" @(#)stat.2 8.4 (Berkeley) 5/1/95
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd February 23, 2021
|
.Dd March 30, 2021
|
||||||
.Dt STAT 2
|
.Dt STAT 2
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -111,6 +111,17 @@ See the description of the
|
|||||||
flag in the
|
flag in the
|
||||||
.Xr open 2
|
.Xr open 2
|
||||||
manual page.
|
manual page.
|
||||||
|
.It Dv AT_EMPTY_PATH
|
||||||
|
If the
|
||||||
|
.Fa path
|
||||||
|
argument is an empty string, operate on the file or directory
|
||||||
|
referenced by the descriptor
|
||||||
|
.Fa fd .
|
||||||
|
If
|
||||||
|
.Fa fd
|
||||||
|
is equal to
|
||||||
|
.Dv AT_FDCWD ,
|
||||||
|
operate on the current working directory.
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
If
|
If
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
.\" @(#)utimes.2 8.1 (Berkeley) 6/4/93
|
.\" @(#)utimes.2 8.1 (Berkeley) 6/4/93
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd February 23, 2021
|
.Dd March 30, 2021
|
||||||
.Dt UTIMENSAT 2
|
.Dt UTIMENSAT 2
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -155,6 +155,17 @@ See the description of the
|
|||||||
flag in the
|
flag in the
|
||||||
.Xr open 2
|
.Xr open 2
|
||||||
manual page.
|
manual page.
|
||||||
|
.It Dv AT_EMPTY_PATH
|
||||||
|
If the
|
||||||
|
.Fa path
|
||||||
|
argument is an empty string, operate on the file or directory
|
||||||
|
referenced by the descriptor
|
||||||
|
.Fa fd .
|
||||||
|
If
|
||||||
|
.Fa fd
|
||||||
|
is equal to
|
||||||
|
.Dv AT_FDCWD ,
|
||||||
|
operate on the current working directory.
|
||||||
.El
|
.El
|
||||||
.Sh RETURN VALUES
|
.Sh RETURN VALUES
|
||||||
.Rv -std
|
.Rv -std
|
||||||
|
@ -401,7 +401,9 @@ namei_setup(struct nameidata *ndp, struct vnode **dpp, struct pwd **pwdp)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
if (error == 0 && (*dpp)->v_type != VDIR)
|
if (error == 0 && (*dpp)->v_type != VDIR &&
|
||||||
|
(cnp->cn_pnbuf[0] != '\0' ||
|
||||||
|
(cnp->cn_flags & EMPTYPATH) == 0))
|
||||||
error = ENOTDIR;
|
error = ENOTDIR;
|
||||||
}
|
}
|
||||||
if (error == 0 && (cnp->cn_flags & RBENEATH) != 0) {
|
if (error == 0 && (cnp->cn_flags & RBENEATH) != 0) {
|
||||||
@ -458,23 +460,62 @@ namei_getpath(struct nameidata *ndp)
|
|||||||
&ndp->ni_pathlen);
|
&ndp->ni_pathlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (__predict_false(error != 0)) {
|
if (__predict_false(error != 0))
|
||||||
namei_cleanup_cnp(cnp);
|
|
||||||
return (error);
|
return (error);
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Don't allow empty pathnames.
|
* Don't allow empty pathnames unless EMPTYPATH is specified.
|
||||||
|
* Caller checks for ENOENT as an indication for the empty path.
|
||||||
*/
|
*/
|
||||||
if (__predict_false(*cnp->cn_pnbuf == '\0')) {
|
if (__predict_false(*cnp->cn_pnbuf == '\0'))
|
||||||
namei_cleanup_cnp(cnp);
|
|
||||||
return (ENOENT);
|
return (ENOENT);
|
||||||
}
|
|
||||||
|
|
||||||
cnp->cn_nameptr = cnp->cn_pnbuf;
|
cnp->cn_nameptr = cnp->cn_pnbuf;
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
namei_emptypath(struct nameidata *ndp)
|
||||||
|
{
|
||||||
|
struct componentname *cnp;
|
||||||
|
struct pwd *pwd;
|
||||||
|
struct vnode *dp;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
cnp = &ndp->ni_cnd;
|
||||||
|
MPASS(*cnp->cn_pnbuf == '\0');
|
||||||
|
MPASS((cnp->cn_flags & EMPTYPATH) != 0);
|
||||||
|
MPASS((cnp->cn_flags & (LOCKPARENT | WANTPARENT)) == 0);
|
||||||
|
|
||||||
|
error = namei_setup(ndp, &dp, &pwd);
|
||||||
|
if (error != 0) {
|
||||||
|
namei_cleanup_cnp(cnp);
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
ndp->ni_vp = dp;
|
||||||
|
vref(dp);
|
||||||
|
namei_cleanup_cnp(cnp);
|
||||||
|
pwd_drop(pwd);
|
||||||
|
ndp->ni_resflags |= NIRES_EMPTYPATH;
|
||||||
|
NDVALIDATE(ndp);
|
||||||
|
if ((cnp->cn_flags & LOCKLEAF) != 0) {
|
||||||
|
VOP_LOCK(dp, (cnp->cn_flags & LOCKSHARED) != 0 ?
|
||||||
|
LK_SHARED : LK_EXCLUSIVE);
|
||||||
|
if (VN_IS_DOOMED(dp)) {
|
||||||
|
vput(dp);
|
||||||
|
error = ENOENT;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SDT_PROBE4(vfs, namei, lookup, return, 0, ndp->ni_vp, false, ndp);
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
errout:
|
||||||
|
SDT_PROBE4(vfs, namei, lookup, return, error, NULL, false, ndp);
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert a pathname into a pointer to a locked vnode.
|
* Convert a pathname into a pointer to a locked vnode.
|
||||||
*
|
*
|
||||||
@ -555,6 +596,11 @@ namei(struct nameidata *ndp)
|
|||||||
|
|
||||||
error = namei_getpath(ndp);
|
error = namei_getpath(ndp);
|
||||||
if (__predict_false(error != 0)) {
|
if (__predict_false(error != 0)) {
|
||||||
|
if (error == ENOENT && (cnp->cn_flags & EMPTYPATH) != 0)
|
||||||
|
return (namei_emptypath(ndp));
|
||||||
|
namei_cleanup_cnp(cnp);
|
||||||
|
SDT_PROBE4(vfs, namei, lookup, return, error, NULL,
|
||||||
|
false, ndp);
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -588,6 +634,7 @@ namei(struct nameidata *ndp)
|
|||||||
ndp->ni_loopcnt = 0;
|
ndp->ni_loopcnt = 0;
|
||||||
error = namei_getpath(ndp);
|
error = namei_getpath(ndp);
|
||||||
if (__predict_false(error != 0)) {
|
if (__predict_false(error != 0)) {
|
||||||
|
namei_cleanup_cnp(cnp);
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
/* FALLTHROUGH */
|
/* FALLTHROUGH */
|
||||||
|
@ -129,6 +129,8 @@ at2cnpflags(u_int at_flags, u_int mask)
|
|||||||
res |= (at_flags & AT_SYMLINK_NOFOLLOW) != 0 ? NOFOLLOW :
|
res |= (at_flags & AT_SYMLINK_NOFOLLOW) != 0 ? NOFOLLOW :
|
||||||
FOLLOW;
|
FOLLOW;
|
||||||
}
|
}
|
||||||
|
if ((mask & AT_EMPTY_PATH) != 0 && (at_flags & AT_EMPTY_PATH) != 0)
|
||||||
|
res |= EMPTYPATH;
|
||||||
return (res);
|
return (res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1496,12 +1498,13 @@ sys_linkat(struct thread *td, struct linkat_args *uap)
|
|||||||
int flag;
|
int flag;
|
||||||
|
|
||||||
flag = uap->flag;
|
flag = uap->flag;
|
||||||
if ((flag & ~(AT_SYMLINK_FOLLOW | AT_RESOLVE_BENEATH)) != 0)
|
if ((flag & ~(AT_SYMLINK_FOLLOW | AT_RESOLVE_BENEATH |
|
||||||
|
AT_EMPTY_PATH)) != 0)
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
return (kern_linkat(td, uap->fd1, uap->fd2, uap->path1, uap->path2,
|
return (kern_linkat(td, uap->fd1, uap->fd2, uap->path1, uap->path2,
|
||||||
UIO_USERSPACE, at2cnpflags(flag, AT_SYMLINK_FOLLOW |
|
UIO_USERSPACE, at2cnpflags(flag, AT_SYMLINK_FOLLOW |
|
||||||
AT_RESOLVE_BENEATH)));
|
AT_RESOLVE_BENEATH | AT_EMPTY_PATH)));
|
||||||
}
|
}
|
||||||
|
|
||||||
int hardlink_check_uid = 0;
|
int hardlink_check_uid = 0;
|
||||||
@ -1578,6 +1581,23 @@ kern_linkat_vp(struct thread *td, struct vnode *vp, int fd, const char *path,
|
|||||||
LOCKPARENT | SAVENAME | AUDITVNODE2 | NOCACHE, segflag, path, fd,
|
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_resflags & NIRES_EMPTYPATH) != 0) {
|
||||||
|
error = priv_check(td, PRIV_VFS_FHOPEN);
|
||||||
|
if (error != 0) {
|
||||||
|
NDFREE(&nd, NDF_ONLY_PNBUF);
|
||||||
|
if (nd.ni_vp != NULL) {
|
||||||
|
if (nd.ni_dvp == nd.ni_vp)
|
||||||
|
vrele(nd.ni_dvp);
|
||||||
|
else
|
||||||
|
vput(nd.ni_dvp);
|
||||||
|
vrele(nd.ni_vp);
|
||||||
|
} else {
|
||||||
|
vput(nd.ni_dvp);
|
||||||
|
}
|
||||||
|
vrele(vp);
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
}
|
||||||
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)
|
||||||
@ -2075,7 +2095,7 @@ kern_accessat(struct thread *td, int fd, const char *path,
|
|||||||
struct nameidata nd;
|
struct nameidata nd;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if ((flag & ~(AT_EACCESS | AT_RESOLVE_BENEATH)) != 0)
|
if ((flag & ~(AT_EACCESS | AT_RESOLVE_BENEATH | AT_EMPTY_PATH)) != 0)
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
if (amode != F_OK && (amode & ~(R_OK | W_OK | X_OK)) != 0)
|
if (amode != F_OK && (amode & ~(R_OK | W_OK | X_OK)) != 0)
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
@ -2096,8 +2116,8 @@ kern_accessat(struct thread *td, int fd, const char *path,
|
|||||||
usecred = cred;
|
usecred = cred;
|
||||||
AUDIT_ARG_VALUE(amode);
|
AUDIT_ARG_VALUE(amode);
|
||||||
NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF |
|
NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF |
|
||||||
AUDITVNODE1 | at2cnpflags(flag, AT_RESOLVE_BENEATH),
|
AUDITVNODE1 | at2cnpflags(flag, AT_RESOLVE_BENEATH |
|
||||||
pathseg, path, fd, &cap_fstat_rights, td);
|
AT_EMPTY_PATH), pathseg, path, fd, &cap_fstat_rights, td);
|
||||||
if ((error = namei(&nd)) != 0)
|
if ((error = namei(&nd)) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
vp = nd.ni_vp;
|
vp = nd.ni_vp;
|
||||||
@ -2387,12 +2407,13 @@ kern_statat(struct thread *td, int flag, int fd, const char *path,
|
|||||||
struct nameidata nd;
|
struct nameidata nd;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH)) != 0)
|
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH |
|
||||||
|
AT_EMPTY_PATH)) != 0)
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_RESOLVE_BENEATH |
|
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_RESOLVE_BENEATH |
|
||||||
AT_SYMLINK_NOFOLLOW) | LOCKSHARED | LOCKLEAF | AUDITVNODE1,
|
AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH) | LOCKSHARED | LOCKLEAF |
|
||||||
pathseg, path, fd, &cap_fstat_rights, td);
|
AUDITVNODE1, pathseg, path, fd, &cap_fstat_rights, td);
|
||||||
|
|
||||||
if ((error = namei(&nd)) != 0)
|
if ((error = namei(&nd)) != 0)
|
||||||
return (error);
|
return (error);
|
||||||
@ -2710,7 +2731,8 @@ int
|
|||||||
sys_chflagsat(struct thread *td, struct chflagsat_args *uap)
|
sys_chflagsat(struct thread *td, struct chflagsat_args *uap)
|
||||||
{
|
{
|
||||||
|
|
||||||
if ((uap->atflag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH)) != 0)
|
if ((uap->atflag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH |
|
||||||
|
AT_EMPTY_PATH)) != 0)
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
return (kern_chflagsat(td, uap->fd, uap->path, UIO_USERSPACE,
|
return (kern_chflagsat(td, uap->fd, uap->path, UIO_USERSPACE,
|
||||||
@ -2743,8 +2765,8 @@ kern_chflagsat(struct thread *td, int fd, const char *path,
|
|||||||
|
|
||||||
AUDIT_ARG_FFLAGS(flags);
|
AUDIT_ARG_FFLAGS(flags);
|
||||||
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(atflag, AT_SYMLINK_NOFOLLOW |
|
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(atflag, AT_SYMLINK_NOFOLLOW |
|
||||||
AT_RESOLVE_BENEATH) | AUDITVNODE1, pathseg, path, fd,
|
AT_RESOLVE_BENEATH | AT_EMPTY_PATH) | AUDITVNODE1, pathseg, path,
|
||||||
&cap_fchflags_rights, td);
|
fd, &cap_fchflags_rights, td);
|
||||||
if ((error = namei(&nd)) != 0)
|
if ((error = namei(&nd)) != 0)
|
||||||
return (error);
|
return (error);
|
||||||
NDFREE_NOTHING(&nd);
|
NDFREE_NOTHING(&nd);
|
||||||
@ -2838,7 +2860,8 @@ int
|
|||||||
sys_fchmodat(struct thread *td, struct fchmodat_args *uap)
|
sys_fchmodat(struct thread *td, struct fchmodat_args *uap)
|
||||||
{
|
{
|
||||||
|
|
||||||
if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH)) != 0)
|
if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH |
|
||||||
|
AT_EMPTY_PATH)) != 0)
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
return (kern_fchmodat(td, uap->fd, uap->path, UIO_USERSPACE,
|
return (kern_fchmodat(td, uap->fd, uap->path, UIO_USERSPACE,
|
||||||
@ -2871,8 +2894,8 @@ kern_fchmodat(struct thread *td, int fd, const char *path,
|
|||||||
|
|
||||||
AUDIT_ARG_MODE(mode);
|
AUDIT_ARG_MODE(mode);
|
||||||
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW |
|
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW |
|
||||||
AT_RESOLVE_BENEATH) | AUDITVNODE1, pathseg, path, fd,
|
AT_RESOLVE_BENEATH | AT_EMPTY_PATH) | AUDITVNODE1, pathseg, path,
|
||||||
&cap_fchmod_rights, td);
|
fd, &cap_fchmod_rights, td);
|
||||||
if ((error = namei(&nd)) != 0)
|
if ((error = namei(&nd)) != 0)
|
||||||
return (error);
|
return (error);
|
||||||
NDFREE_NOTHING(&nd);
|
NDFREE_NOTHING(&nd);
|
||||||
@ -2966,7 +2989,8 @@ int
|
|||||||
sys_fchownat(struct thread *td, struct fchownat_args *uap)
|
sys_fchownat(struct thread *td, struct fchownat_args *uap)
|
||||||
{
|
{
|
||||||
|
|
||||||
if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH)) != 0)
|
if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH |
|
||||||
|
AT_EMPTY_PATH)) != 0)
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
return (kern_fchownat(td, uap->fd, uap->path, UIO_USERSPACE, uap->uid,
|
return (kern_fchownat(td, uap->fd, uap->path, UIO_USERSPACE, uap->uid,
|
||||||
@ -2982,8 +3006,8 @@ kern_fchownat(struct thread *td, int fd, const char *path,
|
|||||||
|
|
||||||
AUDIT_ARG_OWNER(uid, gid);
|
AUDIT_ARG_OWNER(uid, gid);
|
||||||
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW |
|
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW |
|
||||||
AT_RESOLVE_BENEATH) | AUDITVNODE1, pathseg, path, fd,
|
AT_RESOLVE_BENEATH | AT_EMPTY_PATH) | AUDITVNODE1, pathseg, path,
|
||||||
&cap_fchown_rights, td);
|
fd, &cap_fchown_rights, td);
|
||||||
|
|
||||||
if ((error = namei(&nd)) != 0)
|
if ((error = namei(&nd)) != 0)
|
||||||
return (error);
|
return (error);
|
||||||
@ -3334,13 +3358,14 @@ kern_utimensat(struct thread *td, int fd, const char *path,
|
|||||||
struct timespec ts[2];
|
struct timespec ts[2];
|
||||||
int error, flags;
|
int error, flags;
|
||||||
|
|
||||||
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH)) != 0)
|
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH |
|
||||||
|
AT_EMPTY_PATH)) != 0)
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
if ((error = getutimens(tptr, tptrseg, ts, &flags)) != 0)
|
if ((error = getutimens(tptr, tptrseg, ts, &flags)) != 0)
|
||||||
return (error);
|
return (error);
|
||||||
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW |
|
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW |
|
||||||
AT_RESOLVE_BENEATH) | AUDITVNODE1,
|
AT_RESOLVE_BENEATH | AT_EMPTY_PATH) | AUDITVNODE1,
|
||||||
pathseg, path, fd, &cap_futimes_rights, td);
|
pathseg, path, fd, &cap_futimes_rights, td);
|
||||||
if ((error = namei(&nd)) != 0)
|
if ((error = namei(&nd)) != 0)
|
||||||
return (error);
|
return (error);
|
||||||
|
@ -224,6 +224,7 @@ typedef __pid_t pid_t;
|
|||||||
/* #define AT_UNUSED1 0x1000 *//* Was AT_BENEATH */
|
/* #define AT_UNUSED1 0x1000 *//* Was AT_BENEATH */
|
||||||
#define AT_RESOLVE_BENEATH 0x2000 /* Do not allow name resolution
|
#define AT_RESOLVE_BENEATH 0x2000 /* Do not allow name resolution
|
||||||
to walk out of dirfd */
|
to walk out of dirfd */
|
||||||
|
#define AT_EMPTY_PATH 0x4000 /* Operate on dirfd if path is empty */
|
||||||
#endif /* __BSD_VISIBLE */
|
#endif /* __BSD_VISIBLE */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -144,10 +144,12 @@ int cache_fplookup(struct nameidata *ndp, enum cache_fpl_status *status,
|
|||||||
#define WANTPARENT 0x0010 /* want parent vnode returned unlocked */
|
#define WANTPARENT 0x0010 /* want parent vnode returned unlocked */
|
||||||
#define FAILIFEXISTS 0x0020 /* return EEXIST if found */
|
#define FAILIFEXISTS 0x0020 /* return EEXIST if found */
|
||||||
#define FOLLOW 0x0040 /* follow symbolic links */
|
#define FOLLOW 0x0040 /* follow symbolic links */
|
||||||
|
#define EMPTYPATH 0x0080 /* Allow empty path for *at */
|
||||||
#define LOCKSHARED 0x0100 /* Shared lock leaf */
|
#define LOCKSHARED 0x0100 /* Shared lock leaf */
|
||||||
#define NOFOLLOW 0x0000 /* do not follow symbolic links (pseudo) */
|
#define NOFOLLOW 0x0000 /* do not follow symbolic links (pseudo) */
|
||||||
#define RBENEATH 0x100000000ULL /* No escape, even tmp, from start dir */
|
#define RBENEATH 0x100000000ULL /* No escape, even tmp, from start dir */
|
||||||
#define MODMASK 0xf000001ffULL /* mask of operational modifiers */
|
#define MODMASK 0xf000001ffULL /* mask of operational modifiers */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Namei parameter descriptors.
|
* Namei parameter descriptors.
|
||||||
*
|
*
|
||||||
@ -198,6 +200,7 @@ int cache_fplookup(struct nameidata *ndp, enum cache_fpl_status *status,
|
|||||||
*/
|
*/
|
||||||
#define NIRES_ABS 0x00000001 /* Path was absolute */
|
#define NIRES_ABS 0x00000001 /* Path was absolute */
|
||||||
#define NIRES_STRICTREL 0x00000002 /* Restricted lookup result */
|
#define NIRES_STRICTREL 0x00000002 /* Restricted lookup result */
|
||||||
|
#define NIRES_EMPTYPATH 0x00000004 /* EMPTYPATH used */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Flags in ni_lcf, valid for the duration of the namei call.
|
* Flags in ni_lcf, valid for the duration of the namei call.
|
||||||
|
Loading…
Reference in New Issue
Block a user