mirror of
https://github.com/linux-apfs/apfstests.git
synced 2026-05-01 15:01:44 -07:00
QA test attempting to reproduce multiple directory entries problem.
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -155,3 +155,4 @@ ioctl nathans@sgi.com
|
||||
086 log auto
|
||||
087 log auto
|
||||
088 perms
|
||||
089 metadata auto
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+265
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user