QA test attempting to reproduce multiple directory entries problem.
authorfsgqa <fsgqa>
Thu, 22 Apr 2004 03:06:52 +0000 (03:06 +0000)
committerfsgqa <fsgqa>
Thu, 22 Apr 2004 03:06:52 +0000 (03:06 +0000)
089 [new file with mode: 0755]
089.out [new file with mode: 0644]
group
src/Makefile
src/t_mtab.c [new file with mode: 0644]

diff --git a/089 b/089
new file mode 100755 (executable)
index 0000000..1d08cbc
--- /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 (file)
index 0000000..a8c9e44
--- /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 5434b639f8aeef9a2dd58cd94f8fcd1bfabd91c4..a8544bbf0a435e1eba72df46106712555b73f998 100644 (file)
--- a/group
+++ b/group
@@ -155,3 +155,4 @@ ioctl               nathans@sgi.com
 086 log auto
 087 log auto
 088 perms
+089 metadata auto
index 0dfe315029549bad63c8a93eb5de8760943a6a3c..08609e76798497359d7ffb6991f9dbc8377a8a54 100644 (file)
@@ -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 (file)
index 0000000..917ce89
--- /dev/null
@@ -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 <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <mntent.h>
+#include <limits.h>
+
+#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;
+}