1#!/bin/sh 2 3# 4# Copyright (c) 2008-2013 Peter Holm <pho@FreeBSD.org> 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[ `id -u ` -ne 0 ] && echo "Must be root!" && exit 1 30 31. ../default.cfg 32 33# Test scenario by jhb@ 34 35[ -z "$nfs_export" ] && exit 0 36ping -c 2 `echo $nfs_export | sed 's/:.*//'` > /dev/null 2>&1 || 37 exit 0 38 39odir=`pwd` 40cd /tmp 41sed '1,/^EOF/d' < $odir/$0 > nfsrename.c 42mycc -o nfsrename -Wall nfsrename.c 43rm -f nfsrename.c 44cd $odir 45 46mount | grep "$mntpoint" | grep nfs > /dev/null && umount $mntpoint 47mount -t nfs -o tcp -o retrycnt=3 -o intr,soft -o rw $nfs_export $mntpoint 48 49for i in `jot 10`; do 50 /tmp/nfsrename $mntpoint/nfsrename.$i > /dev/null 2>&1 & 51 pids="$pids $!" 52done 53s=0 54for i in $pids; do 55 wait $i 56 [ $? -ne 0 ] && s=1 57done 58pkill nfsrename 59rm -f $mntpoint/nfsrename.* 60 61umount $mntpoint > /dev/null 2>&1 62while mount | grep "$mntpoint" | grep -q nfs; do 63 umount -f $mntpoint > /dev/null 2>&1 64done 65 66rm -f /tmp/nfsrename 67exit $s 68 69EOF 70/* 71 * Try to expose races with doing renames over NFS that require silly 72 * renames. This results in 2 different RENAME RPCs leaving a race 73 * window where the file may not exist. It also appears that FreeBSD 74 * with shared lookups in NFS can get confused and possibly reference 75 * the sillyrenamed file in lookup but the file is deleted by the time 76 * open gets to it. 77 */ 78 79#include <err.h> 80#include <libgen.h> 81#include <limits.h> 82#include <stdio.h> 83#include <stdlib.h> 84#include <time.h> 85#include <unistd.h> 86 87static char *filename; 88static char *dir; 89 90#define RUNTIME 720 91 92static void 93usage(void) 94{ 95 96 fprintf(stderr, "nfsrename: [-n children] file\n"); 97 exit(1); 98} 99 100static void 101read_file(void) 102{ 103 FILE *fp; 104 char buffer[4096]; 105 106 fp = fopen(filename, "r"); 107 if (fp == NULL) { 108 return; 109 } 110 while (!feof(fp)) { 111 if (fread(buffer, sizeof(buffer), 1, fp) < sizeof(buffer)) 112 break; 113 } 114 if (ferror(fp)) 115 warnx("fread encountered an error"); 116 fclose(fp); 117} 118 119static void 120write_file(void) 121{ 122 FILE *fp; 123 char path[1024]; 124 int fd; 125 126 snprintf(path, sizeof(path), "%s/nfsrename.XXXXXX", dir); 127 fd = mkstemp(path); 128 if (fd < 0) { 129 warn("mkstemp"); 130 return; 131 } 132 133 fp = fdopen(fd, "w"); 134 if (fp == NULL) { 135 warn("fdopen:writer"); 136 close(fd); 137 unlink(path); 138 } 139 140 fprintf(fp, "blah blah blah garbage %ld\n", (long)arc4random()); 141 fclose(fp); 142 if (rename(path, filename) < 0) { 143 warn("rename"); 144 unlink(path); 145 } 146} 147 148static void 149random_sleep(int base, int slop) 150{ 151 long val; 152 153 val = arc4random() % slop; 154 usleep(base + val); 155} 156 157static void 158child(void) 159{ 160 time_t start; 161 162 start = time(NULL); 163 for (;;) { 164 random_sleep(500, 50); 165 read_file(); 166 if (time(NULL) - start > RUNTIME) 167 errx(1, "Timed out"); 168 } 169 exit(0); 170} 171 172int 173main(int ac, char **av) 174{ 175 time_t start; 176 long i, nchild; 177 char *cp; 178 int ch; 179 180 nchild = 1; 181 while ((ch = getopt(ac, av, "n:")) != -1) { 182 switch (ch) { 183 case 'n': 184 nchild = strtol(optarg, &cp, 0); 185 if (*cp != '\0') 186 errx(1, "Invalid count %s", optarg); 187 break; 188 case '?': 189 default: 190 usage(); 191 } 192 } 193 ac -= optind; 194 av += optind; 195 196 if (ac == 0) 197 errx(1, "Missing filename"); 198 else if (ac > 1) 199 errx(1, "Extra arguments"); 200 201 filename = av[0]; 202 dir = dirname(filename); 203 srandomdev(); 204 write_file(); 205 206 for (i = 0; i < nchild; i++) { 207 switch (fork()) { 208 case 0: 209 child(); 210 case -1: 211 err(1, "fork"); 212 } 213 } 214 215 start = time(NULL); 216 for (i = 0; i < 10000; i++) { 217 random_sleep(1500, 1000); 218 write_file(); 219 if (time(NULL) - start > RUNTIME) 220 errx(1, "Timed out"); 221 } 222 223 return (0); 224} 225