xref: /freebsd/tools/test/stress2/misc/nullfs33.sh (revision 9c6ed9a88d3afb8bc9aba7cf852cc36e03bcd34c)
1#!/bin/sh
2
3# Bug 275570 - self-referential nullfs mount over tmpfs in combination with MNT_UPDATE results in a hang
4
5# Test scenario mounts:
6# tmpfs on /mnt (tmpfs, local, noexec, read-only)
7# .setup-done on /mnt/.setup-done (nullfs, local)
8
9# Hang seen on a GENERIC kernel only
10# UID  PID PPID C PRI NI   VSZ  RSS MWCHAN STAT TT     TIME COMMAND
11#   0 4429 4417 4  59  0 14192 2832 vlp2   D+    0  0:00.06 ./nullfs33
12# https://people.freebsd.org/~pho/stress/log/log0663.txt
13
14
15. ../default.cfg
16set -u
17prog=$(basename "$0" .sh)
18
19cat > /tmp/$prog.c <<EOF
20#include <assert.h>
21#include <err.h>
22#include <errno.h>
23#include <stdlib.h>
24#include <stdio.h>
25#include <string.h>
26#include <unistd.h>
27#include <sys/_iovec.h>
28#include <sys/file.h>
29#include <sys/mount.h>
30
31static void xmount(const char* fstype, const char* from, const char* to, int flags) {
32
33  char errmsg[255];
34  errmsg[0] = '\0';
35
36  int capacity = 8;
37
38  struct iovec* iov = malloc(sizeof(struct iovec) * capacity);
39
40  iov[0].iov_base = "fstype";
41  iov[0].iov_len  = sizeof("fstype");
42  iov[1].iov_base = __DECONST(char*, fstype);
43  iov[1].iov_len  = strlen(fstype) + 1;
44
45  iov[2].iov_base = "fspath";
46  iov[2].iov_len  = sizeof("fspath");
47  iov[3].iov_base = __DECONST(char*, to);
48  iov[3].iov_len  = strlen(to) + 1;
49
50  iov[4].iov_base = "from";
51  iov[4].iov_len  = sizeof("from");
52  iov[5].iov_base = __DECONST(char*, from);
53  iov[5].iov_len  = strlen(from) + 1;
54
55  iov[6].iov_base = "errmsg";
56  iov[6].iov_len  = sizeof("errmsg");
57  iov[7].iov_base = errmsg;
58  iov[7].iov_len  = sizeof(errmsg);
59
60  int len = 8;
61
62  if (flags & MNT_NOCOVER) {
63
64    assert(len + 2 <= capacity);
65
66    iov[len].iov_base = "nocover";
67    iov[len].iov_len  = sizeof("nocover");
68    len++;
69
70    iov[len].iov_base = NULL;
71    iov[len].iov_len  = 0;
72    len++;
73  }
74
75  assert(len == capacity);
76
77  if (nmount(iov, len, (int)flags) == -1) {
78    err(EXIT_FAILURE, "nmount %s -> %s: %s", from, to, errmsg);
79  }
80
81  free(iov);
82}
83
84static void xchdir(const char* path) {
85  if (chdir(path) == -1) {
86    err(EXIT_FAILURE, "chdir(\"%s\")", path);
87  }
88}
89
90static void xcreat(char* path, int mode) {
91  int fd = open(path, O_CREAT | O_WRONLY, mode);
92  if (fd == -1) {
93    err(EXIT_FAILURE, "can't create %s", path);
94  }
95  close(fd);
96}
97
98//#define ROOT_DIR "/mnt"
99#define ROOT_DIR "$mntpoint"
100
101int main(int argc __unused, char* argv[] __unused) {
102
103  xchdir(ROOT_DIR);
104
105  xmount("tmpfs", "tmpfs", ".", MNT_NOEXEC);
106
107  xcreat(".setup-done", 0444);
108  xmount("nullfs", ".setup-done", ".setup-done", 0);
109
110  xmount("tmpfs", "tmpfs", ".", MNT_RDONLY | MNT_NOEXEC | MNT_UPDATE);
111
112  unmount(ROOT_DIR "/.setup-done", MNT_FORCE);
113  unmount(ROOT_DIR, MNT_FORCE);
114
115  return 0;
116}
117EOF
118cc -o /tmp/$prog -Wall -Wextra -O0 /tmp/$prog.c || exit 1
119
120cd /tmp
121start=`date +%s`
122while [ $((`date +%s` - start)) -lt 60 ]; do
123	./$prog || break
124done
125cd -
126
127mount | grep -q "on $mntpoint " && umount $mntpoint
128rm -f /tmp/$prog /tmp/$prog.c
129exit 0
130