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