kern/176233: New dup3() implementation for FreeBSD

Jukka Ukkonen jau at iki.fi
Mon Feb 18 10:00:02 UTC 2013


>Number:         176233
>Category:       kern
>Synopsis:       New dup3() implementation for FreeBSD
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Mon Feb 18 10:00:00 UTC 2013
>Closed-Date:
>Last-Modified:
>Originator:     Jukka Ukkonen
>Release:        9.1-STABLE
>Organization:
-----
>Environment:
FreeBSD sleipnir 9.1-STABLE FreeBSD 9.1-STABLE #0 r246937M: Mon Feb 18 08:44:50 EET 2013     root at sleipnir:/usr/obj/usr/src/sys/Sleipnir  amd64
>Description:
Because dup3() with the additional flags argument would be useful in general,
here is an implementation for FreeBSD.
Some other UNIX style systems (at least NetBSD and Linux) already have a similar
function. So, there is also the enhanced portability point of view driving this.
>How-To-Repeat:
No actual problem.
Only a nifty add-on feature and enhanced portability.

>Fix:
Find a patch attached.


Patch attached with submission follows:

--- ./lib/libc/gen/Makefile.inc.orig	2013-02-16 09:50:42.000000000 +0200
+++ ./lib/libc/gen/Makefile.inc	2013-02-16 09:50:59.000000000 +0200
@@ -10,7 +10,8 @@
 	alarm.c arc4random.c assert.c aux.c basename.c check_utility_compat.c \
 	clock.c closedir.c confstr.c \
 	crypt.c ctermid.c daemon.c devname.c dirname.c disklabel.c \
-	dlfcn.c drand48.c elf_utils.c erand48.c err.c errlst.c errno.c \
+	dlfcn.c drand48.c dup3.c \
+	elf_utils.c erand48.c err.c errlst.c errno.c \
 	exec.c fdevname.c feature_present.c fmtcheck.c fmtmsg.c fnmatch.c \
 	fpclassify.c frexp.c fstab.c ftok.c fts.c fts-compat.c ftw.c \
 	getbootfile.c getbsize.c \
--- ./lib/libc/gen/Symbol.map.orig	2013-02-16 09:52:41.000000000 +0200
+++ ./lib/libc/gen/Symbol.map	2013-02-16 09:53:25.000000000 +0200
@@ -382,6 +382,7 @@
 FBSD_1.3 {
 	 fdlopen;
 	__FreeBSD_libc_enter_restricted_mode;
+	dup3;
 	getcontextx;
 	gid_from_group;
 	nvis;
--- ./include/unistd.h.orig	2013-02-16 10:02:01.000000000 +0200
+++ ./include/unistd.h	2013-02-16 10:03:13.000000000 +0200
@@ -326,6 +326,9 @@
 void	 closefrom(int);
 int	 dup(int);
 int	 dup2(int, int);
+#if __BSD_VISIBLE
+int	 dup3(int, int, int);
+#endif
 int	 execl(const char *, const char *, ...);
 int	 execle(const char *, const char *, ...);
 int	 execlp(const char *, const char *, ...);
--- lib/libc/sys/Makefile.inc.orig	2013-02-16 10:34:41.000000000 +0200
+++ lib/libc/sys/Makefile.inc	2013-02-16 22:09:12.000000000 +0200
@@ -132,7 +132,7 @@
 MLINKS+=clock_gettime.2 clock_getres.2 clock_gettime.2 clock_settime.2
 MLINKS+=cpuset.2 cpuset_getid.2 cpuset.2 cpuset_setid.2
 MLINKS+=cpuset_getaffinity.2 cpuset_setaffinity.2
-MLINKS+=dup.2 dup2.2
+MLINKS+=dup.2 dup2.2 dup3.2
 MLINKS+=execve.2 fexecve.2
 MLINKS+=extattr_get_file.2 extattr.2 \
 	extattr_get_file.2 extattr_delete_fd.2 \
--- lib/libc/sys/dup.2.orig	2013-02-16 09:59:09.000000000 +0200
+++ lib/libc/sys/dup.2	2013-02-18 11:28:21.000000000 +0200
@@ -43,6 +43,9 @@
 .Fn dup "int oldd"
 .Ft int
 .Fn dup2 "int oldd" "int newd"
+.In fcntl.h
+.Ft int
+.Fn dup3 "int oldd" "int newd" "int flags"
 .Sh DESCRIPTION
 The
 .Fn dup
@@ -116,6 +119,72 @@
 .Fn dup2
 is successful, and does nothing.
 .Pp
+The
+.Fn dup3
+function is otherwise much the same as
+.Fn dup2
+with the added ability to automatically set certain
+important flags to the resulting new file descriptor.
+.br
+By setting the bit
+.Dv O_CLOEXEC
+in the flags one can mark the resulting file descriptor be
+automatically closed during
+.Fn execve
+and other functions in the
+.Fn exec*
+family.
+Since this feature is activated atomically inside the kernel
+there is no risk that even in a threaded code another thread
+could call any of the
+.Fn exec*
+functions before the descriptor has been marked to be closed.
+.br
+Setting the bit
+.Dv O_NONBLOCK
+in the flags marks the resulting file descriptor for non-blocking I/O.
+.sp
+There a couple of features in
+.Fn dup3
+which can have an impact on portability.
+.br
+Currently this implementation of
+.Fn dup3
+does not support the
+.Nx
+feature
+.Dv O_NOSIGPIPE .
+.br
+Because the
+.Fx
+kernel can properly handle the case when
+.Fa oldd
+==
+.Fa newd
+this implementation of
+.Fn dup3 
+will not return an error like 
+.Fn dup3 
+on Linux when it receives two equal file descriptors as arguments.
+Instead this implementation intentionally behaves like
+.Fn dup3
+in
+.Nx
+and sets the flags on the resulting file descriptor.
+Thus any code that has worked on Linux should
+work fine also on
+.Fx
+and/or
+.Nx ,
+but porting to the other direction may cause hiccups.
+.Pp
+Unlike the other two functions
+.Fn dup3
+is not implemented as a genuine system call but as a normal library function
+depending on the features provided by the
+.Fn fcntl
+system call.
+.Pp
 The related
 .Xr cap_new 2
 system call allows file descriptors to be duplicated with restrictions on
@@ -138,6 +207,7 @@
 .It Bq Er EMFILE
 Too many descriptors are active.
 .El
+.sp
 The
 .Fn dup2
 system call fails if:
@@ -149,6 +219,26 @@
 .Fa newd
 argument is negative or exceeds the maximum allowable descriptor number
 .El
+.sp
+The 
+.Fn dup3
+function fails if:
+.Bl -tag -width Er
+.It Bq Er EBADF
+The
+.Fa oldd
+argument is not a valid active descriptor or the
+.Fa newd
+argument is negative or exceeds the maximum allowable descriptor number
+.It Bq Er EINVAL
+The
+.Fa flags
+argument containes unsupported bits.
+Currently the only accepted bits are
+.Dv O_CLOEXEC 
+and
+.Dv O_NONBLOCK .
+.El
 .Sh SEE ALSO
 .Xr accept 2 ,
 .Xr cap_new 2 ,
@@ -166,6 +256,13 @@
 .Fn dup2
 system calls are expected to conform to
 .St -p1003.1-90 .
+.br
+Currently the
+.Fn dup3
+entry is not dictated by any standard.
+It is simply a natural extension the other two variants and
+a compatibility feature to match a similar extension in other
+UNIX like systems.
 .Sh HISTORY
 The
 .Fn dup
@@ -173,3 +270,9 @@
 .Fn dup2
 functions appeared in
 .At v7 .
+.br
+The
+.Fn dup3
+function appeared in
+.Fx 9.1
+STABLE branch.
--- /dev/null	2013-02-16 22:00:01.000000000 +0200
+++ lib/libc/gen/dup3.c	2013-02-16 22:03:55.000000000 +0200
@@ -0,0 +1,123 @@
+/*-
+ * Copyright (c) 2012 Jukka A. Ukkonen
+ * All rights reserved.
+ *
+ * This software was developed by Jukka Ukkonen for FreeBSD.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "namespace.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "un-namespace.h"
+
+/*
+ * A bunch of checks just in case someone
+ * tries to compile this on a too old system.
+ */
+
+#if !defined(F_DUP2FD_CLOEXEC) && defined(_F_DUP2FD_CLOEXEC)
+#  define F_DUP2FD_CLOEXEC  _F_DUP2FD_CLOEXEC
+#endif
+
+#if !defined(F_DUP2FD_CLOEXEC)
+#  error Neither F_DUP2FD_CLOEXEC nor _F_DUP2FD_CLOEXEC defined!
+#endif
+
+#if !defined(F_DUP2FD)
+#  error F_DUP2FD not defined!
+#endif
+
+#if !defined(O_CLOEXEC)
+#  error O_CLOEXEC not defined!
+#endif
+
+/*
+ * In case FreeBSD ever becomes O_NOSIGPIPE aware,
+ * we will instantly start using it.
+ */
+
+#if !defined(O_NOSIGPIPE)
+#  define O_NOSIGPIPE	0
+#endif
+
+int
+__dup3 (oldfd, newfd, flags)
+	int oldfd;
+	int newfd;
+	int flags;
+{
+	int how;
+	int ret;
+
+#if 0
+	/*
+	 * This would be how Linux does it
+	 * in pointlessly restrictive style.
+	 * NetBSD does not do this.
+	 * FreeBSD can also handle this case
+	 * properly.
+	 * ==> Anything which works on Linux
+	 * will work also on {Free,Net}BSD.
+	 * BSD code on Linux may experience
+	 * hiccups, if someone relies on
+	 * oldfd == newfd.
+	 */
+
+	if (oldfd == newfd) {
+		errno = EINVAL;
+		return (-1);
+	}
+#endif
+
+	if (flags & ~(O_CLOEXEC | O_NONBLOCK | O_NOSIGPIPE)) {
+		errno = EINVAL;
+		return (-1);
+	}
+
+	how = (flags & O_CLOEXEC) ? F_DUP2FD_CLOEXEC : F_DUP2FD;
+
+	ret = _fcntl (oldfd, how, newfd);
+
+	if (ret < 0)
+		return (-1);
+
+	newfd = ret;
+
+	if (flags & (O_NONBLOCK | O_NOSIGPIPE)) {
+		int flags2;
+
+		flags2 = _fcntl (newfd, F_GETFL, 0);
+		flags2 |= (flags & (O_NONBLOCK | O_NOSIGPIPE));
+		(void) _fcntl (newfd, F_SETFL, flags2);
+	}
+
+	return (newfd);
+}
+
+__weak_reference(__dup3, dup3);
+__weak_reference(__dup3, _dup3);


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list