1#!/bin/sh
2
3#
4# Copyright (c) 2009 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# $FreeBSD$
29#
30
31# Test case for vfs.lookup_shared=1 that shows possible name cache
32# inconsistency:
33
34# $ ls -l /tmp/file.05015?
35# ls: /tmp/file.050150: No such file or directory
36# $ fsdb -r /dev/ad4s1e
37# ** /dev/ad4s1e (NO WRITE)
38# Examining file system `/dev/ad4s1e'
39# Last Mounted on /tmp
40# current inode: directory
41# I=2 MODE=41777 SIZE=5120
42#         BTIME=May  7 05:54:47 2006 [0 nsec]
43#         MTIME=Apr  2 11:27:36 2009 [0 nsec]
44#         CTIME=Apr  2 11:27:36 2009 [0 nsec]
45#         ATIME=Apr  2 12:00:30 2009 [0 nsec]
46# OWNER=root GRP=wheel LINKCNT=35 FLAGS=0 BLKCNT=c GEN=65f71df4
47# fsdb (inum: 2)> lookup file.050150
48# component `file.050150': current inode: regular file
49# I=198 MODE=100600 SIZE=0
50#         BTIME=Apr  2 11:24:33 2009 [0 nsec]
51#         MTIME=Apr  2 11:24:33 2009 [0 nsec]
52#         CTIME=Apr  2 11:24:33 2009 [0 nsec]
53#         ATIME=Apr  2 11:24:33 2009 [0 nsec]
54# OWNER=pho GRP=wheel LINKCNT=1 FLAGS=0 BLKCNT=0 GEN=1deaab3a
55# fsdb (inum: 198)> quit
56# $
57
58# Consistency is restored by a umount + mount of the FS
59
60# Observations:
61#    No problems seen with vfs.lookup_shared=0.
62#    Does not fail in a "private" subdirectory
63
64. ../default.cfg
65
66odir=`pwd`
67cd /tmp
68sed '1,/^EOF/d' < $odir/$0 > namecache.c
69cc -o namecache -Wall namecache.c
70rm -f namecache.c
71
72#dir=/tmp/namecache.dir	# No problems seen
73dir=/tmp
74[ -d $dir ] || mkdir -p $dir
75cd $dir
76
77for i in `jot 30`; do
78	for j in `jot 10`; do
79		/tmp/namecache &
80	done
81
82	for j in `jot 10`; do
83		wait
84	done
85done
86
87if ls -l ${dir}/file.0* 2>&1 | egrep "file.0[0-9]" | grep -q "No such file"; then
88	echo "ls -l ${dir}/file.0*"
89	ls -l ${dir}/file.0*
90fi
91
92rm -f /tmp/namecache # /${dir}/file.0*
93exit
94EOF
95/* Test scenario for possible name cache problem */
96
97#include <sys/types.h>
98#include <dirent.h>
99#include <err.h>
100#include <fcntl.h>
101#include <signal.h>
102#include <stdio.h>
103#include <stdlib.h>
104#include <string.h>
105#include <sys/param.h>
106#include <sys/stat.h>
107#include <sys/wait.h>
108#include <unistd.h>
109
110static char path[MAXPATHLEN+1];
111static char buf[64 * 1024];
112
113void
114pm(void)
115{
116	int fd, n;
117	int space = sizeof(buf);
118	struct stat statb;
119	long base;
120	struct dirent *dp;
121	char *bp = buf;
122
123	if ((fd = open(".", O_RDONLY)) == -1)
124		err(1, "open(%s)", ".");
125
126	do {
127		if ((n = getdirentries(fd, bp, space, &base)) == -1)
128			err(1, "getdirentries");
129		space = space - n;
130		bp   = bp + n;
131	} while (n != 0);
132	close(fd);
133
134	bp = buf;
135	dp = (struct dirent *)bp;
136	for (;;) {
137		if (strcmp(path, dp->d_name) == 0) {
138
139			if (stat(dp->d_name, &statb) == -1) {
140				warn("stat(%s)", dp->d_name);
141				printf("name: %-10s, inode %7d, type %2d, namelen %d, d_reclen %d\n",
142					dp->d_name, dp->d_fileno, dp->d_type, dp->d_namlen,
143					dp->d_reclen);
144				fflush(stdout);
145			} else {
146				printf("stat(%s) succeeded!\n", path);
147				fflush(stdout);
148			}
149
150		}
151		bp = bp + dp->d_reclen;
152		dp = (struct dirent *)bp;
153		if (dp->d_reclen <= 0)
154			break;
155	}
156}
157
158static void
159reader(void) {
160	int fd;
161
162	if ((fd = open(path, O_RDWR, 0600)) < 0) {
163		warn("open(%s). %s:%d", path, __FILE__, __LINE__);
164		pm();
165		exit(1);
166	}
167	close(fd);
168	return;
169}
170
171static void
172writer(void) {
173	int fd;
174
175	if ((fd = open(path, O_RDWR, 0600)) < 0) {
176		warn("open(%s). %s:%d", path, __FILE__, __LINE__);
177		pm();
178		exit(1);
179	}
180	close(fd);
181	return;
182}
183
184int
185main(int argc, char **argv)
186{
187	pid_t pid;
188	int fd, i, status;
189
190	for (i = 0; i < 10000; i++) {
191		if (sprintf(path, "file.0%d", getpid()) < 0)
192			err(1, "sprintf()");
193		if ((fd = open(path, O_CREAT | O_RDWR, 0600)) == -1)
194			err(1, "open(%s)", path);
195		close(fd);
196
197		if ((pid = fork()) == 0) {
198			writer();
199			exit(EXIT_SUCCESS);
200
201		} else if (pid > 0) {
202			reader();
203			if (waitpid(pid, &status, 0) == -1)
204				warn("waitpid(%d)", pid);
205		} else
206			err(1, "fork(), %s:%d",  __FILE__, __LINE__);
207
208		if (unlink(path) == -1)
209			err(1, "unlink(%s). %s:%d", path, __FILE__, __LINE__);
210	}
211	return (0);
212}
213