xref: /freebsd/tools/test/stress2/misc/flock_open_close.sh (revision 9bc300465e48e19d794d88d0c158a2adb92c7197)
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
49newfs $newfs_flags md$mdstart > /dev/null
50mount /dev/md$mdstart $mntpoint
51chmod 777 $mntpoint
52
53cp /bin/test $mntpoint
54chown $testuser $mntpoint/test
55chmod +w $mntpoint/test
56
57su $testuser -c "/tmp/flock_open_close $mntpoint/test" &
58pid=$!
59while kill -0 $! 2>/dev/null; do
60	mksnap_ffs $mntpoint $mntpoint/.snap/snap
61	sleep 2
62	rm -f $mntpoint/.snap/snap
63	sleep 1
64done
65wait $pid
66s=$?
67
68for i in `jot 10`; do
69	mount | grep -q md$mdstart  && \
70		umount $mntpoint && mdconfig -d -u $mdstart && break
71	sleep 2
72done
73if mount | grep -q md$mdstart; then
74	fstat $mntpoint
75	echo "umount $mntpoint failed"
76	exit 1
77fi
78rm -f /tmp/flock_open_close
79exit $s
80EOF
81
82#include <sys/types.h>
83#include <sys/stat.h>
84#include <sys/wait.h>
85#include <err.h>
86#include <errno.h>
87#include <fcntl.h>
88#include <stdio.h>
89#include <stdlib.h>
90#include <time.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
128	(void)open(av[0], O_RDONLY | O_SHLOCK);
129	execv(av[0], av);
130	/* "flock_open_close: execv(/mnt/test): Text file busy" seen */
131	err(127, "execv(%s)", av[0]);
132}
133
134int
135main(int ac, char **av)
136{
137	struct stat sb;
138	pid_t pid;
139	time_t start;
140	int e, 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	start = time(NULL);
156	while (time(NULL) - start < 150) {
157		pid = fork();
158		if (pid < 0)
159			err(1, "vfork");
160		if (pid == 0)
161			exec_child(av + 1);
162		wait(&status);
163		if (WIFEXITED(status) && WEXITSTATUS(status) == 127) {
164			fprintf(stderr, "FAIL\n");
165			e = 1;
166			break;
167		}
168		if (WIFEXITED(status) && WEXITSTATUS(status) != 1) {
169			/* /bin/test returns 1 */
170			e = 1;
171			break;
172		}
173	}
174	return (e);
175}
176