t_union.c revision 314817
1/*	$NetBSD: t_union.c,v 1.9 2017/01/13 21:30:40 christos Exp $	*/
2
3#include <sys/types.h>
4#include <sys/mount.h>
5
6#include <atf-c.h>
7#include <err.h>
8#include <errno.h>
9#include <fcntl.h>
10#include <stdio.h>
11#include <unistd.h>
12#include <string.h>
13#include <stdlib.h>
14
15#include <rump/rump.h>
16#include <rump/rump_syscalls.h>
17
18#include <miscfs/union/union.h>
19
20#include "h_macros.h"
21#include "../common/h_fsmacros.h"
22
23#define MSTR "magic bus"
24
25static void
26xput_tfile(const char *mp, const char *path)
27{
28	char pathb[MAXPATHLEN];
29	int fd;
30
31	strcpy(pathb, mp);
32	strcat(pathb, "/");
33	strcat(pathb, path);
34
35	RL(fd = rump_sys_open(pathb, O_CREAT | O_RDWR, 0777));
36	if (rump_sys_write(fd, MSTR, sizeof(MSTR)) != sizeof(MSTR))
37		atf_tc_fail_errno("write to testfile");
38	RL(rump_sys_close(fd));
39}
40
41static int
42xread_tfile(const char *mp, const char *path)
43{
44	char pathb[MAXPATHLEN];
45	char buf[128];
46	int fd;
47
48	strcpy(pathb, mp);
49	strcat(pathb, "/");
50	strcat(pathb, path);
51
52	fd = rump_sys_open(pathb, O_RDONLY);
53	if (fd == -1)
54		return errno;
55	if (rump_sys_read(fd, buf, sizeof(buf)) == -1)
56		atf_tc_fail_errno("read tfile");
57	RL(rump_sys_close(fd));
58	if (strcmp(buf, MSTR) == 0)
59		return 0;
60	return EPROGMISMATCH;
61}
62
63/*
64 * Mount a unionfs for testing.  Before calling, "mp" contains
65 * the upper layer.  Lowerpath is constructed so that the directory
66 * contains rumpfs.
67 */
68static void
69mountunion(const char *mp, char *lowerpath)
70{
71	struct union_args unionargs;
72
73	sprintf(lowerpath, "/lower");
74	rump_sys_mkdir(lowerpath, 0777);
75
76	/* mount the union with our testfs as the upper layer */
77	memset(&unionargs, 0, sizeof(unionargs));
78	unionargs.target = lowerpath;
79	unionargs.mntflags = UNMNT_BELOW;
80
81	if (rump_sys_mount(MOUNT_UNION, mp, 0,
82	    &unionargs, sizeof(unionargs)) == -1) {
83		if (errno == EOPNOTSUPP) {
84			atf_tc_skip("fs does not support VOP_WHITEOUT");
85		} else {
86			atf_tc_fail_errno("union mount");
87		}
88	}
89}
90
91#if 0
92static void
93toggleroot(void)
94{
95	static int status;
96
97	status ^= MNT_RDONLY;
98
99	printf("0x%x\n", status);
100	RL(rump_sys_mount(MOUNT_RUMPFS, "/", status | MNT_UPDATE, NULL, 0));
101}
102#endif
103
104#define TFILE "tensti"
105#define TDIR "testdir"
106#define TDFILE TDIR "/indir"
107
108static void
109basic(const atf_tc_t *tc, const char *mp)
110{
111	char lowerpath[MAXPATHLEN];
112	char dbuf[8192];
113	struct stat sb;
114	struct dirent *dp;
115	int error, fd, dsize;
116
117	mountunion(mp, lowerpath);
118
119	/* create a file in the lower layer */
120	xput_tfile(lowerpath, TFILE);
121
122	/* first, test we can read the old file from the new namespace */
123	error = xread_tfile(mp, TFILE);
124	if (error != 0)
125		atf_tc_fail("union compare failed: %d (%s)",
126		    error, strerror(error));
127
128	/* then, test upper layer writes don't affect the lower layer */
129	xput_tfile(mp, "kiekko");
130	if ((error = xread_tfile(lowerpath, "kiekko")) != ENOENT)
131		atf_tc_fail("invisibility failed: %d (%s)",
132		    error, strerror(error));
133
134	/* check that we can whiteout stuff in the upper layer */
135	FSTEST_ENTER();
136	RL(rump_sys_unlink(TFILE));
137	ATF_REQUIRE_ERRNO(ENOENT, rump_sys_stat(TFILE, &sb) == -1);
138	FSTEST_EXIT();
139
140	/* check that the removed node is not in the directory listing */
141	RL(fd = rump_sys_open(mp, O_RDONLY));
142	RL(dsize = rump_sys_getdents(fd, dbuf, sizeof(dbuf)));
143	for (dp = (struct dirent *)dbuf;
144	    (char *)dp < dbuf + dsize;
145	    dp = _DIRENT_NEXT(dp)) {
146		if (strcmp(dp->d_name, TFILE) == 0 && dp->d_type != DT_WHT)
147			atf_tc_fail("removed file non-white-outed");
148	}
149	RL(rump_sys_close(fd));
150
151	RL(rump_sys_unmount(mp, 0));
152}
153
154static void
155whiteout(const atf_tc_t *tc, const char *mp)
156{
157	char lower[MAXPATHLEN];
158	struct stat sb;
159	void *fsarg;
160
161	/*
162	 * XXX: use ffs here to make sure any screwups in rumpfs don't
163	 * affect the test
164	 */
165	RL(ffs_fstest_newfs(tc, &fsarg, "daimage", 1024*1024*5, NULL));
166	RL(ffs_fstest_mount(tc, fsarg, "/lower", 0));
167
168	/* create a file in the lower layer */
169	RL(rump_sys_chdir("/lower"));
170	RL(rump_sys_mkdir(TDIR, 0777));
171	RL(rump_sys_mkdir(TDFILE, 0777));
172	RL(rump_sys_chdir("/"));
173
174	RL(ffs_fstest_unmount(tc, "/lower", 0));
175	RL(ffs_fstest_mount(tc, fsarg, "/lower", MNT_RDONLY));
176
177	mountunion(mp, lower);
178
179	FSTEST_ENTER();
180	ATF_REQUIRE_ERRNO(ENOTEMPTY, rump_sys_rmdir(TDIR) == -1);
181	RL(rump_sys_rmdir(TDFILE));
182	RL(rump_sys_rmdir(TDIR));
183	ATF_REQUIRE_ERRNO(ENOENT, rump_sys_stat(TDFILE, &sb) == -1);
184	ATF_REQUIRE_ERRNO(ENOENT, rump_sys_stat(TDIR, &sb) == -1);
185
186	RL(rump_sys_mkdir(TDIR, 0777));
187	RL(rump_sys_stat(TDIR, &sb));
188	ATF_REQUIRE_ERRNO(ENOENT, rump_sys_stat(TDFILE, &sb) == -1);
189	FSTEST_EXIT();
190
191	RL(rump_sys_unmount(mp, 0));
192}
193
194ATF_TC_FSAPPLY(basic, "check basic union functionality");
195ATF_TC_FSAPPLY(whiteout, "create whiteout in upper layer");
196
197ATF_TP_ADD_TCS(tp)
198{
199
200	ATF_TP_FSAPPLY(basic);
201	ATF_TP_FSAPPLY(whiteout);
202
203	return atf_no_error();
204}
205