Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F131904110
D29111.id86561.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
D29111.id86561.diff
View Options
diff --git a/lib/libc/sys/access.2 b/lib/libc/sys/access.2
--- a/lib/libc/sys/access.2
+++ b/lib/libc/sys/access.2
@@ -28,7 +28,7 @@
.\" @(#)access.2 8.2 (Berkeley) 4/1/94
.\" $FreeBSD$
.\"
-.Dd February 23, 2021
+.Dd March 30, 2021
.Dt ACCESS 2
.Os
.Sh NAME
@@ -129,6 +129,17 @@
flag in the
.Xr open 2
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
.Pp
Even if a process's real or effective user has appropriate privileges
diff --git a/lib/libc/sys/chflags.2 b/lib/libc/sys/chflags.2
--- a/lib/libc/sys/chflags.2
+++ b/lib/libc/sys/chflags.2
@@ -28,7 +28,7 @@
.\" @(#)chflags.2 8.3 (Berkeley) 5/2/95
.\" $FreeBSD$
.\"
-.Dd February 23, 2021
+.Dd March 30, 2021
.Dt CHFLAGS 2
.Os
.Sh NAME
@@ -103,6 +103,17 @@
flag in the
.Xr open 2
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
.Pp
If
diff --git a/lib/libc/sys/chmod.2 b/lib/libc/sys/chmod.2
--- a/lib/libc/sys/chmod.2
+++ b/lib/libc/sys/chmod.2
@@ -28,7 +28,7 @@
.\" @(#)chmod.2 8.1 (Berkeley) 6/4/93
.\" $FreeBSD$
.\"
-.Dd February 23, 2021
+.Dd March 30, 2021
.Dt CHMOD 2
.Os
.Sh NAME
@@ -110,6 +110,17 @@
flag in the
.Xr open 2
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
.Pp
If
diff --git a/lib/libc/sys/chown.2 b/lib/libc/sys/chown.2
--- a/lib/libc/sys/chown.2
+++ b/lib/libc/sys/chown.2
@@ -28,7 +28,7 @@
.\" @(#)chown.2 8.4 (Berkeley) 4/19/94
.\" $FreeBSD$
.\"
-.Dd February 23, 2021
+.Dd March 30, 2021
.Dt CHOWN 2
.Os
.Sh NAME
@@ -127,6 +127,17 @@
flag in the
.Xr open 2
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
.Pp
If
diff --git a/lib/libc/sys/link.2 b/lib/libc/sys/link.2
--- a/lib/libc/sys/link.2
+++ b/lib/libc/sys/link.2
@@ -28,7 +28,7 @@
.\" @(#)link.2 8.3 (Berkeley) 1/12/94
.\" $FreeBSD$
.\"
-.Dd February 23, 2021
+.Dd March 30, 2021
.Dt LINK 2
.Os
.Sh NAME
@@ -124,6 +124,15 @@
flag in the
.Xr open 2
manual page.
+.It Dv AT_EMPTY_PATH
+If the
+.Fa path
+argument is an empty string, link the file referenced by the descriptor
+.Fa fd .
+The operation requires that the calling process has the
+.Dv PRIV_VFS_FHOPEN
+privilege, effectively being executed with effective user
+.Dv root .
.El
.Pp
If
diff --git a/lib/libc/sys/stat.2 b/lib/libc/sys/stat.2
--- a/lib/libc/sys/stat.2
+++ b/lib/libc/sys/stat.2
@@ -28,7 +28,7 @@
.\" @(#)stat.2 8.4 (Berkeley) 5/1/95
.\" $FreeBSD$
.\"
-.Dd February 23, 2021
+.Dd March 30, 2021
.Dt STAT 2
.Os
.Sh NAME
@@ -111,6 +111,17 @@
flag in the
.Xr open 2
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
.Pp
If
diff --git a/lib/libc/sys/utimensat.2 b/lib/libc/sys/utimensat.2
--- a/lib/libc/sys/utimensat.2
+++ b/lib/libc/sys/utimensat.2
@@ -31,7 +31,7 @@
.\" @(#)utimes.2 8.1 (Berkeley) 6/4/93
.\" $FreeBSD$
.\"
-.Dd February 23, 2021
+.Dd March 30, 2021
.Dt UTIMENSAT 2
.Os
.Sh NAME
@@ -155,6 +155,17 @@
flag in the
.Xr open 2
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
.Sh RETURN VALUES
.Rv -std
diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c
--- a/sys/kern/vfs_lookup.c
+++ b/sys/kern/vfs_lookup.c
@@ -320,6 +320,7 @@
ndp->ni_lcf |= NI_LCF_STRICTRELATIVE;
ndp->ni_resflags |= NIRES_STRICTREL;
if (ndp->ni_dirfd == AT_FDCWD) {
+ /* XXXKIB allow for EMPTYPATH ? */
#ifdef KTRACE
if (KTRPOINT(td, KTR_CAPFAIL))
ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL);
@@ -401,7 +402,9 @@
}
#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;
}
if (error == 0 && (cnp->cn_flags & RBENEATH) != 0) {
@@ -458,23 +461,62 @@
&ndp->ni_pathlen);
}
- if (__predict_false(error != 0)) {
- namei_cleanup_cnp(cnp);
+ if (__predict_false(error != 0))
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')) {
- namei_cleanup_cnp(cnp);
+ if (__predict_false(*cnp->cn_pnbuf == '\0'))
return (ENOENT);
- }
cnp->cn_nameptr = cnp->cn_pnbuf;
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.
*
@@ -555,6 +597,11 @@
error = namei_getpath(ndp);
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);
}
@@ -588,6 +635,7 @@
ndp->ni_loopcnt = 0;
error = namei_getpath(ndp);
if (__predict_false(error != 0)) {
+ namei_cleanup_cnp(cnp);
return (error);
}
/* FALLTHROUGH */
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -129,6 +129,8 @@
res |= (at_flags & AT_SYMLINK_NOFOLLOW) != 0 ? NOFOLLOW :
FOLLOW;
}
+ if ((mask & AT_EMPTY_PATH) != 0 && (at_flags & AT_EMPTY_PATH) != 0)
+ res |= EMPTYPATH;
return (res);
}
@@ -1496,12 +1498,13 @@
int 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 (kern_linkat(td, uap->fd1, uap->fd2, uap->path1, uap->path2,
UIO_USERSPACE, at2cnpflags(flag, AT_SYMLINK_FOLLOW |
- AT_RESOLVE_BENEATH)));
+ AT_RESOLVE_BENEATH | AT_EMPTY_PATH)));
}
int hardlink_check_uid = 0;
@@ -1578,6 +1581,23 @@
LOCKPARENT | SAVENAME | AUDITVNODE2 | NOCACHE, segflag, path, fd,
&cap_linkat_target_rights, td);
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) {
NDFREE(&nd, NDF_ONLY_PNBUF);
if (nd.ni_dvp == nd.ni_vp)
@@ -2075,7 +2095,7 @@
struct nameidata nd;
int error;
- if ((flag & ~(AT_EACCESS | AT_RESOLVE_BENEATH)) != 0)
+ if ((flag & ~(AT_EACCESS | AT_RESOLVE_BENEATH | AT_EMPTY_PATH)) != 0)
return (EINVAL);
if (amode != F_OK && (amode & ~(R_OK | W_OK | X_OK)) != 0)
return (EINVAL);
@@ -2096,8 +2116,8 @@
usecred = cred;
AUDIT_ARG_VALUE(amode);
NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF |
- AUDITVNODE1 | at2cnpflags(flag, AT_RESOLVE_BENEATH),
- pathseg, path, fd, &cap_fstat_rights, td);
+ AUDITVNODE1 | at2cnpflags(flag, AT_RESOLVE_BENEATH |
+ AT_EMPTY_PATH), pathseg, path, fd, &cap_fstat_rights, td);
if ((error = namei(&nd)) != 0)
goto out;
vp = nd.ni_vp;
@@ -2387,12 +2407,13 @@
struct nameidata nd;
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);
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_RESOLVE_BENEATH |
- AT_SYMLINK_NOFOLLOW) | LOCKSHARED | LOCKLEAF | AUDITVNODE1,
- pathseg, path, fd, &cap_fstat_rights, td);
+ AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH) | LOCKSHARED | LOCKLEAF |
+ AUDITVNODE1, pathseg, path, fd, &cap_fstat_rights, td);
if ((error = namei(&nd)) != 0)
return (error);
@@ -2710,7 +2731,8 @@
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 (kern_chflagsat(td, uap->fd, uap->path, UIO_USERSPACE,
@@ -2743,8 +2765,8 @@
AUDIT_ARG_FFLAGS(flags);
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(atflag, AT_SYMLINK_NOFOLLOW |
- AT_RESOLVE_BENEATH) | AUDITVNODE1, pathseg, path, fd,
- &cap_fchflags_rights, td);
+ AT_RESOLVE_BENEATH | AT_EMPTY_PATH) | AUDITVNODE1, pathseg, path,
+ fd, &cap_fchflags_rights, td);
if ((error = namei(&nd)) != 0)
return (error);
NDFREE_NOTHING(&nd);
@@ -2838,7 +2860,8 @@
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 (kern_fchmodat(td, uap->fd, uap->path, UIO_USERSPACE,
@@ -2871,8 +2894,8 @@
AUDIT_ARG_MODE(mode);
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW |
- AT_RESOLVE_BENEATH) | AUDITVNODE1, pathseg, path, fd,
- &cap_fchmod_rights, td);
+ AT_RESOLVE_BENEATH | AT_EMPTY_PATH) | AUDITVNODE1, pathseg, path,
+ fd, &cap_fchmod_rights, td);
if ((error = namei(&nd)) != 0)
return (error);
NDFREE_NOTHING(&nd);
@@ -2966,7 +2989,8 @@
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 (kern_fchownat(td, uap->fd, uap->path, UIO_USERSPACE, uap->uid,
@@ -2982,8 +3006,8 @@
AUDIT_ARG_OWNER(uid, gid);
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW |
- AT_RESOLVE_BENEATH) | AUDITVNODE1, pathseg, path, fd,
- &cap_fchown_rights, td);
+ AT_RESOLVE_BENEATH | AT_EMPTY_PATH) | AUDITVNODE1, pathseg, path,
+ fd, &cap_fchown_rights, td);
if ((error = namei(&nd)) != 0)
return (error);
@@ -3334,13 +3358,14 @@
struct timespec ts[2];
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);
if ((error = getutimens(tptr, tptrseg, ts, &flags)) != 0)
return (error);
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);
if ((error = namei(&nd)) != 0)
return (error);
diff --git a/sys/sys/fcntl.h b/sys/sys/fcntl.h
--- a/sys/sys/fcntl.h
+++ b/sys/sys/fcntl.h
@@ -219,10 +219,13 @@
#define AT_SYMLINK_NOFOLLOW 0x0200 /* Do not follow symbolic links */
#define AT_SYMLINK_FOLLOW 0x0400 /* Follow symbolic link */
#define AT_REMOVEDIR 0x0800 /* Remove directory instead of file */
+#endif /* __POSIX_VISIBLE >= 200809 */
+#if __BSD_VISIBLE
/* #define AT_UNUSED1 0x1000 *//* Was AT_BENEATH */
#define AT_RESOLVE_BENEATH 0x2000 /* Do not allow name resolution
to walk out of dirfd */
-#endif
+#define AT_EMPTY_PATH 0x4000 /* Operate on dirfd if path is empty */
+#endif /* __BSD_VISIBLE */
/*
* Constants used for fcntl(2)
diff --git a/sys/sys/namei.h b/sys/sys/namei.h
--- a/sys/sys/namei.h
+++ b/sys/sys/namei.h
@@ -144,10 +144,12 @@
#define WANTPARENT 0x0010 /* want parent vnode returned unlocked */
#define FAILIFEXISTS 0x0020 /* return EEXIST if found */
#define FOLLOW 0x0040 /* follow symbolic links */
+#define EMPTYPATH 0x0080 /* Allow empty path for *at */
#define LOCKSHARED 0x0100 /* Shared lock leaf */
#define NOFOLLOW 0x0000 /* do not follow symbolic links (pseudo) */
#define RBENEATH 0x100000000ULL /* No escape, even tmp, from start dir */
#define MODMASK 0xf000001ffULL /* mask of operational modifiers */
+
/*
* Namei parameter descriptors.
*
@@ -198,6 +200,7 @@
*/
#define NIRES_ABS 0x00000001 /* Path was absolute */
#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.
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Oct 13, 2:47 AM (12 h, 36 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
23653175
Default Alt Text
D29111.id86561.diff (13 KB)
Attached To
Mode
D29111: Add AT_EMPTY_PATH
Attached
Detach File
Event Timeline
Log In to Comment