summaryrefslogtreecommitdiff
path: root/regress/sys/kern/dup2_self/dup2_self.c
blob: 5849abe5e86021c2b78464dd64ba00f8ea542b72 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/*	$OpenBSD: dup2_self.c,v 1.3 2003/07/31 21:48:08 deraadt Exp $	*/
/*
 *	Written by Artur Grabowski <art@openbsd.org> 2002 Public Domain.
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <err.h>
#include <fcntl.h>

/*
 * We're testing a small tweak in dup2 semantics. Normally dup and dup2
 * will clear the close-on-exec flag on the new fd (which appears to be
 * an implementation mistake from start and not some planned behavior).
 * In todays implementations of dup and dup2 we have to make an effort
 * to really clear that flag. But all tested implementations of dup2 have
 * another tweak. If we dup2(old, new) when old == new, the syscall
 * short-circuits and returns early (because there is no need to do all
 * the work (and there is a risk for serious mistakes)). So although the
 * docs say that dup2 should "take 'old', close 'new' perform a dup(2) of
 * 'old' into 'new'" the docs are not really followed because close-on-exec
 * is not cleared on 'new'.
 *
 * Since everyone has this bug, we pretend that this is the way it is
 * supposed to be and test here that it really works that way.
 *
 * This is a fine example on where two separate implementation fuckups
 * take out each other and make the end-result the way it was meant to be.
 */

int
main(int argc, char *argv[])
{
	int orgfd, fd1, fd2;
	char temp[] = "/tmp/dup2XXXXXXXXX";

	if ((orgfd = mkstemp(temp)) < 0)
		err(1, "mkstemp");
	remove(temp);

	if (ftruncate(orgfd, 1024) != 0)
		err(1, "ftruncate");

	if ((fd1 = dup(orgfd)) < 0)
		err(1, "dup");

	/* Set close-on-exec */
	if (fcntl(fd1, F_SETFD, 1) != 0)
		err(1, "fcntl(F_SETFD)");

	if ((fd2 = dup2(fd1, fd1)) < 0)
		err(1, "dup2");

	/* Test 1: Do we get the right fd? */
	if (fd2 != fd1)
		errx(1, "dup2 didn't give us the right fd");

	/* Test 2: Was close-on-exec cleared? */
	if (fcntl(fd2, F_GETFD) == 0)
		errx(1, "dup2 cleared close-on-exec");

	return 0;
}