xref: /freebsd/tools/test/stress2/misc/flock_open_close.sh (revision ec0ea6efa1ad229d75c394c1a9b9cac33af2b1d3)
1#!/bin/sh
2
3#
4# Copyright (c) 2012 Peter Holm
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10# 1. Redistributions of source code must retain the above copyright
11#    notice, this list of conditions and the following disclaimer.
12# 2. Redistributions in binary form must reproduce the above copyright
13#    notice, this list of conditions and the following disclaimer in the
14#    documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26# SUCH DAMAGE.
27#
28
29# Demonstrate that close() of an flock'd file is not atomic.
30# Fails with "flock_open_close: execv(/mnt/test): Text file busy"
31
32# Test scenario by: jhb
33
34[ `id -u ` -ne 0 ] && echo "Must be root!" && exit 1
35
36. ../default.cfg
37
38odir=`pwd`
39cd /tmp
40sed '1,/^EOF/d' < $odir/$0 > flock_open_close.c
41rm -f /tmp/flock_open_close
42mycc -o flock_open_close -Wall -Wextra -O2 -g flock_open_close.c -lpthread || exit 1
43rm -f flock_open_close.c
44
45mount | grep $mntpoint | grep -q /dev/md && umount -f $mntpoint
46mdconfig -l | grep -q md$mdstart &&  mdconfig -d -u $mdstart
47
48mdconfig -a -t swap -s 1g -u $mdstart || exit 1
49bsdlabel -w md$mdstart auto
50newfs $newfs_flags md${mdstart}$part > /dev/null
51mount /dev/md${mdstart}$part $mntpoint
52chmod 777 $mntpoint
53
54cp /bin/test $mntpoint
55chown $testuser $mntpoint/test
56chmod +w $mntpoint/test
57
58su $testuser -c "/tmp/flock_open_close $mntpoint/test" &
59pid=$!
60while kill -0 $! 2>/dev/null; do
61	mksnap_ffs $mntpoint $mntpoint/.snap/snap
62	sleep 2
63	rm -f $mntpoint/.snap/snap
64	sleep 1
65done
66wait $pid
67s=$?
68
69for i in `jot 10`; do
70	mount | grep -q md${mdstart}$part  && \
71		umount $mntpoint && mdconfig -d -u $mdstart && break
72	sleep 2
73done
74if mount | grep -q md${mdstart}$part; then
75	fstat $mntpoint
76	echo "umount $mntpoint failed"
77	exit 1
78fi
79rm -f /tmp/flock_open_close
80exit $s
81EOF
82
83#include <sys/types.h>
84#include <sys/stat.h>
85#include <sys/wait.h>
86#include <err.h>
87#include <errno.h>
88#include <fcntl.h>
89#include <stdio.h>
90#include <stdlib.h>
91#include <unistd.h>
92
93static void
94usage(void)
95{
96	fprintf(stderr, "Usage: flock_close_race <binary> [args]\n");
97	exit(1);
98}
99
100static void
101child(const char *binary)
102{
103	int fd;
104
105	/* Exit as soon as our parent exits. */
106	while (getppid() != 1) {
107		fd = open(binary, O_RDWR | O_EXLOCK);
108		if (fd < 0) {
109			/*
110			 * This may get ETXTBSY since exit() will
111			 * close its open fd's (thus releasing the
112			 * lock), before it releases the vmspace (and
113			 * mapping of the binary).
114			 */
115			if (errno == ETXTBSY)
116				continue;
117			err(2, "can't open %s", binary);
118		}
119		close(fd);
120	}
121	exit(0);
122}
123
124static void
125exec_child(char **av)
126{
127	int fd;
128
129	fd = open(av[0], O_RDONLY | O_SHLOCK);
130	execv(av[0], av);
131	/* "flock_open_close: execv(/mnt/test): Text file busy" seen */
132	err(127, "execv(%s)", av[0]);
133}
134
135int
136main(int ac, char **av)
137{
138	struct stat sb;
139	pid_t pid;
140	int e, i, status;
141
142	if (ac < 2)
143		usage();
144	if (stat(av[1], &sb) != 0)
145		err(1, "stat(%s)", av[1]);
146	if (!S_ISREG(sb.st_mode))
147		errx(1, "%s not an executable", av[1]);
148
149	pid = fork();
150	if (pid < 0)
151		err(1, "fork");
152	if (pid == 0)
153		child(av[1]);
154	e = 0;
155	for (i = 0; i < 200000; i++) {
156		pid = fork();
157		if (pid < 0)
158			err(1, "vfork");
159		if (pid == 0)
160			exec_child(av + 1);
161		wait(&status);
162		if (WIFEXITED(status) && WEXITSTATUS(status) == 127) {
163			fprintf(stderr, "FAIL\n");
164			e = 1;
165			break;
166		}
167		if (WIFEXITED(status) && WEXITSTATUS(status) != 1) {
168			/* /bin/test returns 1 */
169			e = 1;
170			break;
171		}
172	}
173	return (e);
174}
175