From: fsgqa Date: Thu, 22 Apr 2004 03:06:52 +0000 (+0000) Subject: QA test attempting to reproduce multiple directory entries problem. X-Git-Tag: v1.1.0~848 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=abdf9cb068e9a2051ab5cc59efe7ec1bf52a7913;p=users%2Fhch%2Fxfstests-dev.git QA test attempting to reproduce multiple directory entries problem. --- diff --git a/089 b/089 new file mode 100755 index 000000000..1d08cbcba --- /dev/null +++ b/089 @@ -0,0 +1,75 @@ +#! /bin/sh +# XFS QA Test No. 089 +# +# Emulate the way Linux mount manipulates /etc/mtab to attempt to +# reproduce a possible bug in rename (see src/t_mtab.c). +# +#----------------------------------------------------------------------- +# Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# Further, this software is distributed without any warranty that it is +# free of the rightful claim of any third person regarding infringement +# or the like. Any license provided herein, whether implied or +# otherwise, applies only to this software file. Patent licenses, if +# any, provided herein do not apply to combinations of this program with +# other software, or any other product whatsoever. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write the Free Software Foundation, Inc., 59 +# Temple Place - Suite 330, Boston MA 02111-1307, USA. +# +# Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, +# Mountain View, CA 94043, or: +# +# http://www.sgi.com +# +# For further information regarding this notice, see: +# +# http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ +#----------------------------------------------------------------------- +# +# creator +owner=nathans@sgi.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! +trap "rm -f $tmp.*; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +# real QA test starts here +[ "X$TEST_DIR" = "X" ] && exit 1 +cd $TEST_DIR +rm -fr test +mkdir test || exit 1 +cd $TEST_DIR/test +mount > t_mtab + +$here/src/t_mtab 2 & +$here/src/t_mtab 2 & +$here/src/t_mtab 2 & +wait + +$here/src/t_mtab 10000 + +echo directory entries: +ls +ls -li > $seq.full + +# success, all done +status=0 +exit diff --git a/089.out b/089.out new file mode 100644 index 000000000..a8c9e4412 --- /dev/null +++ b/089.out @@ -0,0 +1,7 @@ +QA output created by 089 +completed 2 iterations +completed 2 iterations +completed 2 iterations +completed 10000 iterations +directory entries: +t_mtab diff --git a/group b/group index 5434b639f..a8544bbf0 100644 --- a/group +++ b/group @@ -155,3 +155,4 @@ ioctl nathans@sgi.com 086 log auto 087 log auto 088 perms +089 metadata auto diff --git a/src/Makefile b/src/Makefile index 0dfe31502..08609e767 100644 --- a/src/Makefile +++ b/src/Makefile @@ -38,7 +38,7 @@ TARGETS = alloc acl_get bstat devzero dirstress fault feature \ nametest permname randholes runas truncfile usemem \ fstest mmapcat append_reader append_writer \ dirperf metaperf enospc_unlink resvtest scaleread \ - godown t_access_root + godown t_access_root t_mtab ifeq ($(ENABLE_DBM), yes) TARGETS += dbtest endif diff --git a/src/t_mtab.c b/src/t_mtab.c new file mode 100644 index 000000000..917ce8999 --- /dev/null +++ b/src/t_mtab.c @@ -0,0 +1,265 @@ +/* + * Test program based on Linux mount(8) source attempting to + * trigger a suspected problem in rename(2) code paths - its + * symptoms have been multiple mtab entries in /etc... hence + * use of the actual mount code here. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOCK_TIMEOUT 10 +#define _(x) (x) + +static char *mounted = "t_mtab"; +static char *mounted_lock = "t_mtab~"; +static char *mounted_temp = "t_mtab.tmp"; + +/* Updating mtab ----------------------------------------------*/ + +/* Flag for already existing lock file. */ +static int we_created_lockfile = 0; + +/* Flag to indicate that signals have been set up. */ +static int signals_have_been_setup = 0; + +/* Ensure that the lock is released if we are interrupted. */ +static void +handler (int sig) { + fprintf(stderr, "%s", sys_siglist[sig]); + exit(1); +} + +static void +setlkw_timeout (int sig) { + /* nothing, fcntl will fail anyway */ +} + +/* Create the lock file. + The lock file will be removed if we catch a signal or when we exit. */ +/* The old code here used flock on a lock file /etc/mtab~ and deleted + this lock file afterwards. However, as rgooch remarks, that has a + race: a second mount may be waiting on the lock and proceed as + soon as the lock file is deleted by the first mount, and immediately + afterwards a third mount comes, creates a new /etc/mtab~, applies + flock to that, and also proceeds, so that the second and third mount + now both are scribbling in /etc/mtab. + The new code uses a link() instead of a creat(), where we proceed + only if it was us that created the lock, and hence we always have + to delete the lock afterwards. Now the use of flock() is in principle + superfluous, but avoids an arbitrary sleep(). */ + +void +lock_mtab (void) { + int tries = 3; + char linktargetfile[PATH_MAX + 20]; + + if (!signals_have_been_setup) { + int sig = 0; + struct sigaction sa; + + sa.sa_handler = handler; + sa.sa_flags = 0; + sigfillset (&sa.sa_mask); + + while (sigismember (&sa.sa_mask, ++sig) != -1 + && sig != SIGCHLD) { + if (sig == SIGALRM) + sa.sa_handler = setlkw_timeout; + else + sa.sa_handler = handler; + sigaction (sig, &sa, (struct sigaction *) 0); + } + signals_have_been_setup = 1; + } + + /* use 20 as upper bound for the length of %d output */ + snprintf(linktargetfile, PATH_MAX+20, "%s%d", mounted_lock, getpid()); + + /* Repeat until it was us who made the link */ + while (!we_created_lockfile) { + struct flock flock; + int fd, errsv, i, j; + + i = open (linktargetfile, O_WRONLY|O_CREAT, 0); + if (i < 0) { + int errsv = errno; + /* linktargetfile does not exist (as a file) + and we cannot create it. Read-only filesystem? + Too many files open in the system? + Filesystem full? */ + fprintf(stderr, "can't create lock file %s: %s " + "(use -n flag to override)", + linktargetfile, strerror (errsv)); + exit(1); + } + close(i); + + j = link(linktargetfile, mounted_lock); + errsv = errno; + + (void) unlink(linktargetfile); + + if (j < 0 && errsv != EEXIST) { + fprintf(stderr, "can't link lock file %s: %s " + "(use -n flag to override)", + mounted_lock, strerror (errsv)); + exit(1); + } + + fd = open (mounted_lock, O_WRONLY); + + if (fd < 0) { + int errsv = errno; + /* Strange... Maybe the file was just deleted? */ + if (errno == ENOENT && tries-- > 0) + continue; + fprintf(stderr, "can't open lock file %s: %s " + "(use -n flag to override)", + mounted_lock, strerror (errsv)); + exit(1); + } + + flock.l_type = F_WRLCK; + flock.l_whence = SEEK_SET; + flock.l_start = 0; + flock.l_len = 0; + + if (j == 0) { + /* We made the link. Now claim the lock. */ + if (fcntl (fd, F_SETLK, &flock) == -1) { + int errsv = errno; + printf(_("Can't lock lock file %s: %s\n"), + mounted_lock, strerror (errsv)); + /* proceed anyway */ + } + we_created_lockfile = 1; + } else { + static int tries = 0; + + /* Someone else made the link. Wait. */ + alarm(LOCK_TIMEOUT); + if (fcntl (fd, F_SETLKW, &flock) == -1) { + int errsv = errno; + fprintf(stderr, "can't lock lock file %s: %s", + mounted_lock, (errno == EINTR) ? + _("timed out") : strerror (errsv)); + exit(1); + } + alarm(0); + /* Limit the number of iterations - maybe there + still is some old /etc/mtab~ */ + if (tries++ > 3) { + if (tries > 5) { + fprintf(stderr, "Cant create link %s\n" + "Perhaps there is a stale lock file?\n", + mounted_lock); + exit(1); + } + sleep(1); + } + } + close (fd); + } +} + +/* Remove lock file. */ +void +unlock_mtab (void) { + if (we_created_lockfile) { + unlink (mounted_lock); + we_created_lockfile = 0; + } +} + +/* + * Update the mtab. + * Used by umount with null INSTEAD: remove the last DIR entry. + * Used by mount upon a remount: update option part, + * and complain if a wrong device or type was given. + * [Note that often a remount will be a rw remount of / + * where there was no entry before, and we'll have to believe + * the values given in INSTEAD.] + */ + +void +update_mtab (void) +{ + FILE *mntent_fp, *mftmp; + char buffer[4096]; + int size; + + lock_mtab(); + + /* having locked mtab, read it again & write to mtemp */ + mntent_fp = fopen(mounted, "r"); + if (!mntent_fp) { + fprintf(stderr, "cannot open %s for reading\n", mounted); + exit(1); + } + mftmp = fopen(mounted_temp, "w"); + if (!mftmp) { + fprintf(stderr, "cannot open %s for writing\n", mounted_temp); + exit(1); + } + while ((size = read(fileno(mntent_fp), buffer, sizeof(buffer))) > 0) { + if (write(fileno(mftmp), buffer, size) < 0) { + fprintf(stderr, "write failure: %s\n", strerror(errno)); + exit(1); + } + } + if (size < 0) { + fprintf(stderr, "read failure: %s\n", strerror(errno)); + exit(1); + } + fclose(mntent_fp); + + if (fchmod (fileno (mftmp), + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) { + int errsv = errno; + fprintf(stderr, _("error changing mode of %s: %s\n"), + mounted_temp, strerror (errsv)); + } + fclose(mftmp); + + { /* + * If mount is setuid and some non-root user mounts sth, + * then mtab.tmp might get the group of this user. Copy uid/gid + * from the present mtab before renaming. + */ + struct stat sbuf; + if (stat (mounted, &sbuf) == 0) + chown (mounted_temp, sbuf.st_uid, sbuf.st_gid); + } + + /* rename mtemp to mtab */ + if (rename (mounted_temp, mounted) < 0) { + int errsv = errno; + fprintf(stderr, _("can't rename %s to %s: %s\n"), + mounted_temp, mounted, strerror(errsv)); + } + + unlock_mtab(); +} + +int main(int argc, char **argv) +{ + int i, stop = 100000; + + if (argc > 1) + stop = atoi(argv[1]); + + for (i = 0; i < stop; i++) { + update_mtab(); + } + printf("completed %d iterations\n", stop); + return 0; +}