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