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 <unistd.h> 91 92static void 93usage(void) 94{ 95 fprintf(stderr, "Usage: flock_close_race <binary> [args]\n"); 96 exit(1); 97} 98 99static void 100child(const char *binary) 101{ 102 int fd; 103 104 /* Exit as soon as our parent exits. */ 105 while (getppid() != 1) { 106 fd = open(binary, O_RDWR | O_EXLOCK); 107 if (fd < 0) { 108 /* 109 * This may get ETXTBSY since exit() will 110 * close its open fd's (thus releasing the 111 * lock), before it releases the vmspace (and 112 * mapping of the binary). 113 */ 114 if (errno == ETXTBSY) 115 continue; 116 err(2, "can't open %s", binary); 117 } 118 close(fd); 119 } 120 exit(0); 121} 122 123static void 124exec_child(char **av) 125{ 126 127 (void)open(av[0], O_RDONLY | O_SHLOCK); 128 execv(av[0], av); 129 /* "flock_open_close: execv(/mnt/test): Text file busy" seen */ 130 err(127, "execv(%s)", av[0]); 131} 132 133int 134main(int ac, char **av) 135{ 136 struct stat sb; 137 pid_t pid; 138 int e, i, status; 139 140 if (ac < 2) 141 usage(); 142 if (stat(av[1], &sb) != 0) 143 err(1, "stat(%s)", av[1]); 144 if (!S_ISREG(sb.st_mode)) 145 errx(1, "%s not an executable", av[1]); 146 147 pid = fork(); 148 if (pid < 0) 149 err(1, "fork"); 150 if (pid == 0) 151 child(av[1]); 152 e = 0; 153 for (i = 0; i < 200000; i++) { 154 pid = fork(); 155 if (pid < 0) 156 err(1, "vfork"); 157 if (pid == 0) 158 exec_child(av + 1); 159 wait(&status); 160 if (WIFEXITED(status) && WEXITSTATUS(status) == 127) { 161 fprintf(stderr, "FAIL\n"); 162 e = 1; 163 break; 164 } 165 if (WIFEXITED(status) && WEXITSTATUS(status) != 1) { 166 /* /bin/test returns 1 */ 167 e = 1; 168 break; 169 } 170 } 171 return (e); 172} 173