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:
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to