Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 6 Apr 2019 09:34:26 +0000 (UTC)
From:      Mariusz Zaborski <oshogbo@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r345982 - in head: include lib/libc/sys sys/cddl/compat/opensolaris/sys sys/compat/cloudabi sys/compat/freebsd32 sys/compat/linux sys/kern sys/sys sys/ufs/ffs
Message-ID:  <201904060934.x369YQn9043498@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: oshogbo
Date: Sat Apr  6 09:34:26 2019
New Revision: 345982
URL: https://svnweb.freebsd.org/changeset/base/345982

Log:
  Introduce funlinkat syscall that always us to check if we are removing
  the file associated with the given file descriptor.
  
  Reviewed by:	kib, asomers
  Reviewed by:	cem, jilles, brooks (they reviewed previous version)
  Discussed with:	pjd, and many others
  Differential Revision:	https://reviews.freebsd.org/D14567

Modified:
  head/include/unistd.h
  head/lib/libc/sys/Makefile.inc
  head/lib/libc/sys/Symbol.map
  head/lib/libc/sys/unlink.2
  head/sys/cddl/compat/opensolaris/sys/vnode.h
  head/sys/compat/cloudabi/cloudabi_file.c
  head/sys/compat/freebsd32/syscalls.master
  head/sys/compat/linux/linux_file.c
  head/sys/kern/capabilities.conf
  head/sys/kern/syscalls.master
  head/sys/kern/vfs_mountroot.c
  head/sys/kern/vfs_syscalls.c
  head/sys/sys/fcntl.h
  head/sys/sys/syscallsubr.h
  head/sys/ufs/ffs/ffs_alloc.c

Modified: head/include/unistd.h
==============================================================================
--- head/include/unistd.h	Sat Apr  6 09:00:06 2019	(r345981)
+++ head/include/unistd.h	Sat Apr  6 09:34:26 2019	(r345982)
@@ -585,6 +585,7 @@ off_t	 __syscall(quad_t, ...);
 int	 undelete(const char *);
 int	 unwhiteout(const char *);
 void	*valloc(size_t);			/* obsoleted by malloc() */
+int	 funlinkat(int, const char *, int, int);
 
 #ifndef _OPTRESET_DECLARED
 #define	_OPTRESET_DECLARED

Modified: head/lib/libc/sys/Makefile.inc
==============================================================================
--- head/lib/libc/sys/Makefile.inc	Sat Apr  6 09:00:06 2019	(r345981)
+++ head/lib/libc/sys/Makefile.inc	Sat Apr  6 09:34:26 2019	(r345982)
@@ -485,6 +485,7 @@ MLINKS+=timer_settime.2 timer_getoverrun.2 \
 MLINKS+=thr_kill.2 thr_kill2.2
 MLINKS+=truncate.2 ftruncate.2
 MLINKS+=unlink.2 unlinkat.2
+MLINKS+=unlink.2 funlinkat.2
 MLINKS+=utimensat.2 futimens.2
 MLINKS+=utimes.2 futimes.2 \
 	utimes.2 futimesat.2 \

Modified: head/lib/libc/sys/Symbol.map
==============================================================================
--- head/lib/libc/sys/Symbol.map	Sat Apr  6 09:00:06 2019	(r345981)
+++ head/lib/libc/sys/Symbol.map	Sat Apr  6 09:34:26 2019	(r345982)
@@ -406,6 +406,7 @@ FBSD_1.6 {
 	fhlinkat;
 	fhreadlink;
 	getfhat;
+	funlinkat;
 };
 
 FBSDprivate_1.0 {

Modified: head/lib/libc/sys/unlink.2
==============================================================================
--- head/lib/libc/sys/unlink.2	Sat Apr  6 09:00:06 2019	(r345981)
+++ head/lib/libc/sys/unlink.2	Sat Apr  6 09:34:26 2019	(r345982)
@@ -28,7 +28,7 @@
 .\"     @(#)unlink.2	8.1 (Berkeley) 6/4/93
 .\" $FreeBSD$
 .\"
-.Dd November 11, 2018
+.Dd April 6, 2019
 .Dt UNLINK 2
 .Os
 .Sh NAME
@@ -42,7 +42,9 @@
 .Ft int
 .Fn unlink "const char *path"
 .Ft int
-.Fn unlinkat "int fd" "const char *path" "int flag"
+.Fn unlinkat "int dfd" "const char *path" "int flag"
+.Ft int
+.Fn funlinkat "int dfd" "const char *path" "int fd" "int flag"
 .Sh DESCRIPTION
 The
 .Fn unlink
@@ -74,7 +76,7 @@ except in the case where
 specifies a relative path.
 In this case the directory entry to be removed is determined
 relative to the directory associated with the file descriptor
-.Fa fd
+.Fa dfd
 instead of the current working directory.
 .Pp
 The values for
@@ -113,6 +115,26 @@ or
 respectively, depending on whether or not the
 .Dv AT_REMOVEDIR
 bit is set in flag.
+.Pp
+The
+.Fn funlinkat
+system call can be used to unlink an already-opened file, unless that
+file has been replaced since it was opened.
+It is equivalent to
+.Fn unlinkat
+in the case where
+.Fa path
+is already open as the file descriptor
+.Fa fd .
+Otherwise, the path will not be removed and an error will be returned.
+The
+.Fa fd
+can be set the
+.Dv FD_NONE .
+In that case
+.Fn funlinkat
+behaves exactly like
+.Fn unlinkat .
 .Sh RETURN VALUES
 .Rv -std unlink
 .Sh ERRORS
@@ -227,6 +249,15 @@ or the relative
 .Fa path
 escapes it.
 .El
+.Pp
+In addition to the errors returned by
+.Fn unlinkat ,
+.Fn funlinkat
+may fail if:
+.Bl -tag -width Er
+.It Bq Er EDEADLK
+The file descriptor is not associated with the path.
+.El
 .Sh SEE ALSO
 .Xr chflags 2 ,
 .Xr close 2 ,
@@ -246,6 +277,10 @@ The
 .Fn unlinkat
 system call appeared in
 .Fx 8.0 .
+The
+.Fn funlinkat
+system call appeared in
+.Fx 13.0 .
 .Pp
 The
 .Fn unlink

Modified: head/sys/cddl/compat/opensolaris/sys/vnode.h
==============================================================================
--- head/sys/cddl/compat/opensolaris/sys/vnode.h	Sat Apr  6 09:00:06 2019	(r345981)
+++ head/sys/cddl/compat/opensolaris/sys/vnode.h	Sat Apr  6 09:34:26 2019	(r345982)
@@ -278,7 +278,8 @@ vn_remove(char *fnamep, enum uio_seg seg, enum rm dirf
 	ASSERT(seg == UIO_SYSSPACE);
 	ASSERT(dirflag == RMFILE);
 
-	return (kern_unlinkat(curthread, AT_FDCWD, fnamep, seg, 0, 0));
+	return (kern_funlinkat(curthread, AT_FDCWD, fnamep, FD_NONE, seg, 0,
+	    0));
 }
 
 #endif	/* _KERNEL */

Modified: head/sys/compat/cloudabi/cloudabi_file.c
==============================================================================
--- head/sys/compat/cloudabi/cloudabi_file.c	Sat Apr  6 09:00:06 2019	(r345981)
+++ head/sys/compat/cloudabi/cloudabi_file.c	Sat Apr  6 09:34:26 2019	(r345982)
@@ -752,9 +752,11 @@ cloudabi_sys_file_unlink(struct thread *td,
 		return (error);
 
 	if (uap->flags & CLOUDABI_UNLINK_REMOVEDIR)
-		error = kern_rmdirat(td, uap->fd, path, UIO_SYSSPACE, 0);
+		error = kern_frmdirat(td, uap->fd, path, FD_NONE,
+		    UIO_SYSSPACE, 0);
 	else
-		error = kern_unlinkat(td, uap->fd, path, UIO_SYSSPACE, 0, 0);
+		error = kern_funlinkat(td, uap->fd, path, FD_NONE,
+		    UIO_SYSSPACE, 0, 0);
 	cloudabi_freestr(path);
 	return (error);
 }

Modified: head/sys/compat/freebsd32/syscalls.master
==============================================================================
--- head/sys/compat/freebsd32/syscalls.master	Sat Apr  6 09:00:06 2019	(r345981)
+++ head/sys/compat/freebsd32/syscalls.master	Sat Apr  6 09:34:26 2019	(r345982)
@@ -1145,5 +1145,7 @@
 				    const char *to); }
 567	AUE_NULL	NOPROTO { int fhreadlink( struct fhandle *fhp, char *buf, \
 				    size_t bufsize); }
+568	AUE_UNLINKAT	NOPROTO { int funlinkat(int dfd, const char *path, int fd, \
+				    int flag); }
 
 ; vim: syntax=off

Modified: head/sys/compat/linux/linux_file.c
==============================================================================
--- head/sys/compat/linux/linux_file.c	Sat Apr  6 09:00:06 2019	(r345981)
+++ head/sys/compat/linux/linux_file.c	Sat Apr  6 09:34:26 2019	(r345982)
@@ -590,7 +590,7 @@ linux_unlink(struct thread *td, struct linux_unlink_ar
 		printf(ARGS(unlink, "%s"), path);
 #endif
 
-	error = kern_unlinkat(td, AT_FDCWD, path, UIO_SYSSPACE, 0, 0);
+	error = kern_funlinkat(td, AT_FDCWD, path, FD_NONE, UIO_SYSSPACE, 0, 0);
 	if (error == EPERM) {
 		/* Introduce POSIX noncompliant behaviour of Linux */
 		if (kern_statat(td, 0, AT_FDCWD, path, UIO_SYSSPACE, &st,
@@ -623,9 +623,10 @@ linux_unlinkat(struct thread *td, struct linux_unlinka
 #endif
 
 	if (args->flag & LINUX_AT_REMOVEDIR)
-		error = kern_rmdirat(td, dfd, path, UIO_SYSSPACE, 0);
+		error = kern_frmdirat(td, dfd, path, FD_NONE, UIO_SYSSPACE, 0);
 	else
-		error = kern_unlinkat(td, dfd, path, UIO_SYSSPACE, 0, 0);
+		error = kern_funlinkat(td, dfd, path, FD_NONE, UIO_SYSSPACE, 0,
+		    0);
 	if (error == EPERM && !(args->flag & LINUX_AT_REMOVEDIR)) {
 		/* Introduce POSIX noncompliant behaviour of Linux */
 		if (kern_statat(td, AT_SYMLINK_NOFOLLOW, dfd, path,
@@ -741,7 +742,7 @@ linux_rmdir(struct thread *td, struct linux_rmdir_args
 	if (ldebug(rmdir))
 		printf(ARGS(rmdir, "%s"), path);
 #endif
-	error = kern_rmdirat(td, AT_FDCWD, path, UIO_SYSSPACE, 0);
+	error = kern_frmdirat(td, AT_FDCWD, path, FD_NONE, UIO_SYSSPACE, 0);
 	LFREEPATH(path);
 	return (error);
 }

Modified: head/sys/kern/capabilities.conf
==============================================================================
--- head/sys/kern/capabilities.conf	Sat Apr  6 09:00:06 2019	(r345981)
+++ head/sys/kern/capabilities.conf	Sat Apr  6 09:34:26 2019	(r345982)
@@ -468,6 +468,7 @@ readlinkat
 renameat
 symlinkat
 unlinkat
+funlinkat
 utimensat
 
 ##

Modified: head/sys/kern/syscalls.master
==============================================================================
--- head/sys/kern/syscalls.master	Sat Apr  6 09:00:06 2019	(r345981)
+++ head/sys/kern/syscalls.master	Sat Apr  6 09:34:26 2019	(r345982)
@@ -3167,6 +3167,14 @@
 		    size_t bufsize
 		);
 	}
+568	AUE_UNLINKAT	STD {
+		int funlinkat(
+		    int dfd,
+		    _In_z_ const char *path,
+		    int fd,
+		    int flag
+		);
+	}
 
 ; Please copy any additions and changes to the following compatability tables:
 ; sys/compat/freebsd32/syscalls.master

Modified: head/sys/kern/vfs_mountroot.c
==============================================================================
--- head/sys/kern/vfs_mountroot.c	Sat Apr  6 09:00:06 2019	(r345981)
+++ head/sys/kern/vfs_mountroot.c	Sat Apr  6 09:34:26 2019	(r345982)
@@ -389,7 +389,7 @@ vfs_mountroot_shuffle(struct thread *td, struct mount 
 	if (mporoot == mpdevfs) {
 		vfs_unbusy(mpdevfs);
 		/* Unlink the no longer needed /dev/dev -> / symlink */
-		error = kern_unlinkat(td, AT_FDCWD, "/dev/dev",
+		error = kern_funlinkat(td, AT_FDCWD, "/dev/dev", FD_NONE,
 		    UIO_SYSSPACE, 0, 0);
 		if (error)
 			printf("mountroot: unable to unlink /dev/dev "

Modified: head/sys/kern/vfs_syscalls.c
==============================================================================
--- head/sys/kern/vfs_syscalls.c	Sat Apr  6 09:00:06 2019	(r345981)
+++ head/sys/kern/vfs_syscalls.c	Sat Apr  6 09:34:26 2019	(r345982)
@@ -1751,9 +1751,24 @@ int
 sys_unlink(struct thread *td, struct unlink_args *uap)
 {
 
-	return (kern_unlinkat(td, AT_FDCWD, uap->path, UIO_USERSPACE, 0, 0));
+	return (kern_funlinkat(td, AT_FDCWD, uap->path, FD_NONE, UIO_USERSPACE,
+	    0, 0));
 }
 
+static int
+kern_funlinkat_ex(struct thread *td, int dfd, const char *path, int fd,
+    int flag, enum uio_seg pathseg, ino_t oldinum)
+{
+
+	if ((flag & ~AT_REMOVEDIR) != 0)
+		return (EINVAL);
+
+	if ((flag & AT_REMOVEDIR) != 0)
+		return (kern_frmdirat(td, dfd, path, fd, UIO_USERSPACE, 0));
+
+	return (kern_funlinkat(td, dfd, path, fd, UIO_USERSPACE, 0, 0));
+}
+
 #ifndef _SYS_SYSPROTO_H_
 struct unlinkat_args {
 	int	fd;
@@ -1764,46 +1779,69 @@ struct unlinkat_args {
 int
 sys_unlinkat(struct thread *td, struct unlinkat_args *uap)
 {
-	int fd, flag;
-	const char *path;
 
-	flag = uap->flag;
-	fd = uap->fd;
-	path = uap->path;
+	return (kern_funlinkat_ex(td, uap->fd, uap->path, FD_NONE, uap->flag,
+	    UIO_USERSPACE, 0));
+}
 
-	if ((flag & ~(AT_REMOVEDIR | AT_BENEATH)) != 0)
-		return (EINVAL);
+#ifndef _SYS_SYSPROTO_H_
+struct funlinkat_args {
+	int		dfd;
+	const char	*path;
+	int		fd;
+	int		flag;
+};
+#endif
+int
+sys_funlinkat(struct thread *td, struct funlinkat_args *uap)
+{
 
-	if ((uap->flag & AT_REMOVEDIR) != 0)
-		return (kern_rmdirat(td, fd, path, UIO_USERSPACE, flag));
-	else
-		return (kern_unlinkat(td, fd, path, UIO_USERSPACE, flag, 0));
+	return (kern_funlinkat_ex(td, uap->dfd, uap->path, uap->fd, uap->flag,
+	    UIO_USERSPACE, 0));
 }
 
 int
-kern_unlinkat(struct thread *td, int fd, const char *path,
+kern_funlinkat(struct thread *td, int dfd, const char *path, int fd,
     enum uio_seg pathseg, int flag, ino_t oldinum)
 {
 	struct mount *mp;
+	struct file *fp;
 	struct vnode *vp;
 	struct nameidata nd;
 	struct stat sb;
+	cap_rights_t rights;
 	int error;
 
+	fp = NULL;
+	if (fd != FD_NONE) {
+		error = getvnode(td, fd, cap_rights_init(&rights, CAP_LOOKUP),
+		    &fp);
+		if (error != 0)
+			return (error);
+	}
+
 restart:
 	bwillwrite();
 	NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1 |
 	    ((flag & AT_BENEATH) != 0 ? BENEATH : 0),
-	    pathseg, path, fd, &cap_unlinkat_rights, td);
-	if ((error = namei(&nd)) != 0)
-		return (error == EINVAL ? EPERM : error);
+	    pathseg, path, dfd, &cap_unlinkat_rights, td);
+	if ((error = namei(&nd)) != 0) {
+		if (error == EINVAL)
+			error = EPERM;
+		goto fdout;
+	}
 	vp = nd.ni_vp;
 	if (vp->v_type == VDIR && oldinum == 0) {
 		error = EPERM;		/* POSIX */
 	} else if (oldinum != 0 &&
 		  ((error = vn_stat(vp, &sb, td->td_ucred, NOCRED, td)) == 0) &&
 		  sb.st_ino != oldinum) {
-			error = EIDRM;	/* Identifier removed */
+		error = EIDRM;	/* Identifier removed */
+	} else if (fp != NULL && fp->f_vnode != vp) {
+		if ((fp->f_vnode->v_iflag & VI_DOOMED) != 0)
+			error = EBADF;
+		else
+			error = EDEADLK;
 	} else {
 		/*
 		 * The root of a mounted filesystem cannot be deleted.
@@ -1822,8 +1860,9 @@ restart:
 			else
 				vput(vp);
 			if ((error = vn_start_write(NULL, &mp,
-			    V_XSLEEP | PCATCH)) != 0)
-				return (error);
+			    V_XSLEEP | PCATCH)) != 0) {
+				goto fdout;
+			}
 			goto restart;
 		}
 #ifdef MAC
@@ -1845,6 +1884,9 @@ out:
 		vrele(vp);
 	else
 		vput(vp);
+fdout:
+	if (fp != NULL)
+		fdrop(fp, td);
 	return (error);
 }
 
@@ -3704,25 +3746,36 @@ int
 sys_rmdir(struct thread *td, struct rmdir_args *uap)
 {
 
-	return (kern_rmdirat(td, AT_FDCWD, uap->path, UIO_USERSPACE, 0));
+	return (kern_frmdirat(td, AT_FDCWD, uap->path, FD_NONE, UIO_USERSPACE,
+	    0));
 }
 
 int
-kern_rmdirat(struct thread *td, int fd, const char *path, enum uio_seg pathseg,
-    int flag)
+kern_frmdirat(struct thread *td, int dfd, const char *path, int fd,
+    enum uio_seg pathseg, int flag)
 {
 	struct mount *mp;
 	struct vnode *vp;
+	struct file *fp;
 	struct nameidata nd;
+	cap_rights_t rights;
 	int error;
 
+	fp = NULL;
+	if (fd != FD_NONE) {
+		error = getvnode(td, fd, cap_rights_init(&rights, CAP_LOOKUP),
+		    &fp);
+		if (error != 0)
+			return (error);
+	}
+
 restart:
 	bwillwrite();
 	NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1 |
 	    ((flag & AT_BENEATH) != 0 ? BENEATH : 0),
-	    pathseg, path, fd, &cap_unlinkat_rights, td);
+	    pathseg, path, dfd, &cap_unlinkat_rights, td);
 	if ((error = namei(&nd)) != 0)
-		return (error);
+		goto fdout;
 	vp = nd.ni_vp;
 	if (vp->v_type != VDIR) {
 		error = ENOTDIR;
@@ -3742,6 +3795,15 @@ restart:
 		error = EBUSY;
 		goto out;
 	}
+
+	if (fp != NULL && fp->f_vnode != vp) {
+		if ((fp->f_vnode->v_iflag & VI_DOOMED) != 0)
+			error = EBADF;
+		else
+			error = EDEADLK;
+		goto out;
+	}
+
 #ifdef MAC
 	error = mac_vnode_check_unlink(td->td_ucred, nd.ni_dvp, vp,
 	    &nd.ni_cnd);
@@ -3756,7 +3818,7 @@ restart:
 		else
 			vput(nd.ni_dvp);
 		if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0)
-			return (error);
+			goto fdout;
 		goto restart;
 	}
 	vfs_notify_upper(vp, VFS_NOTIFY_UPPER_UNLINK);
@@ -3769,6 +3831,9 @@ out:
 		vrele(nd.ni_dvp);
 	else
 		vput(nd.ni_dvp);
+fdout:
+	if (fp != NULL)
+		fdrop(fp, td);
 	return (error);
 }
 

Modified: head/sys/sys/fcntl.h
==============================================================================
--- head/sys/sys/fcntl.h	Sat Apr  6 09:00:06 2019	(r345981)
+++ head/sys/sys/fcntl.h	Sat Apr  6 09:34:26 2019	(r345982)
@@ -316,6 +316,16 @@ struct __oflock {
 #define	POSIX_FADV_NOREUSE	5	/* access data only once */
 #endif
 
+
+#ifdef __BSD_VISIBLE
+/*
+ * Magic value that specify that corresponding file descriptor to filename
+ * is unknown and sanitary check should be omitted in the funlinkat() and
+ * similar syscalls.
+ */
+#define	FD_NONE			-200
+#endif
+
 #ifndef _KERNEL
 __BEGIN_DECLS
 int	open(const char *, int, ...);

Modified: head/sys/sys/syscallsubr.h
==============================================================================
--- head/sys/sys/syscallsubr.h	Sat Apr  6 09:00:06 2019	(r345981)
+++ head/sys/sys/syscallsubr.h	Sat Apr  6 09:34:26 2019	(r345982)
@@ -218,7 +218,7 @@ int	kern_recvit(struct thread *td, int s, struct msghd
 	    enum uio_seg fromseg, struct mbuf **controlp);
 int	kern_renameat(struct thread *td, int oldfd, const char *old, int newfd,
 	    const char *new, enum uio_seg pathseg);
-int	kern_rmdirat(struct thread *td, int fd, const char *path,
+int	kern_frmdirat(struct thread *td, int dfd, const char *path, int fd,
 	    enum uio_seg pathseg, int flag);
 int	kern_sched_getparam(struct thread *td, struct thread *targettd,
 	    struct sched_param *param);
@@ -285,7 +285,7 @@ int	kern_thr_new(struct thread *td, struct thr_param *
 int	kern_thr_suspend(struct thread *td, struct timespec *tsp);
 int	kern_truncate(struct thread *td, const char *path,
 	    enum uio_seg pathseg, off_t length);
-int	kern_unlinkat(struct thread *td, int fd, const char *path,
+int	kern_funlinkat(struct thread *td, int dfd, const char *path, int fd,
 	    enum uio_seg pathseg, int flag, ino_t oldinum);
 int	kern_utimesat(struct thread *td, int fd, const char *path,
 	    enum uio_seg pathseg, struct timeval *tptr, enum uio_seg tptrseg);

Modified: head/sys/ufs/ffs/ffs_alloc.c
==============================================================================
--- head/sys/ufs/ffs/ffs_alloc.c	Sat Apr  6 09:00:06 2019	(r345981)
+++ head/sys/ufs/ffs/ffs_alloc.c	Sat Apr  6 09:34:26 2019	(r345982)
@@ -3431,14 +3431,15 @@ sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS)
 		}
 #endif /* DEBUG */
 		/*
-		 * kern_unlinkat will do its own start/finish writes and
+		 * kern_funlinkat will do its own start/finish writes and
 		 * they do not nest, so drop ours here. Setting mp == NULL
 		 * indicates that vn_finished_write is not needed down below.
 		 */
 		vn_finished_write(mp);
 		mp = NULL;
-		error = kern_unlinkat(td, AT_FDCWD, (char *)(intptr_t)cmd.value,
-		    UIO_USERSPACE, 0, (ino_t)cmd.size);
+		error = kern_funlinkat(td, AT_FDCWD,
+		    (char *)(intptr_t)cmd.value, FD_NONE, UIO_USERSPACE,
+		    0, (ino_t)cmd.size);
 		break;
 
 	case FFS_SET_INODE:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201904060934.x369YQn9043498>