xref: /netbsd-src/tests/fs/vfs/t_union.c (revision c54cb81102ced2313cb40993fe05548aca9933a1)
1*c54cb811Schristos /*	$NetBSD: t_union.c,v 1.9 2017/01/13 21:30:40 christos Exp $	*/
2b23eef38Spooka 
3b23eef38Spooka #include <sys/types.h>
4b23eef38Spooka #include <sys/mount.h>
5b23eef38Spooka 
6b23eef38Spooka #include <atf-c.h>
7b23eef38Spooka #include <err.h>
8b23eef38Spooka #include <errno.h>
9b23eef38Spooka #include <fcntl.h>
10b23eef38Spooka #include <stdio.h>
11b23eef38Spooka #include <unistd.h>
12b23eef38Spooka #include <string.h>
13b23eef38Spooka #include <stdlib.h>
14b23eef38Spooka 
15b23eef38Spooka #include <rump/rump.h>
16b23eef38Spooka #include <rump/rump_syscalls.h>
17b23eef38Spooka 
18b23eef38Spooka #include <miscfs/union/union.h>
19b23eef38Spooka 
20*c54cb811Schristos #include "h_macros.h"
21b23eef38Spooka #include "../common/h_fsmacros.h"
22b23eef38Spooka 
23b23eef38Spooka #define MSTR "magic bus"
24b23eef38Spooka 
25b23eef38Spooka static void
xput_tfile(const char * mp,const char * path)26b23eef38Spooka xput_tfile(const char *mp, const char *path)
27b23eef38Spooka {
28b23eef38Spooka 	char pathb[MAXPATHLEN];
29b23eef38Spooka 	int fd;
30b23eef38Spooka 
31b23eef38Spooka 	strcpy(pathb, mp);
32b23eef38Spooka 	strcat(pathb, "/");
33b23eef38Spooka 	strcat(pathb, path);
34b23eef38Spooka 
35b23eef38Spooka 	RL(fd = rump_sys_open(pathb, O_CREAT | O_RDWR, 0777));
36b23eef38Spooka 	if (rump_sys_write(fd, MSTR, sizeof(MSTR)) != sizeof(MSTR))
37b23eef38Spooka 		atf_tc_fail_errno("write to testfile");
38b23eef38Spooka 	RL(rump_sys_close(fd));
39b23eef38Spooka }
40b23eef38Spooka 
41b23eef38Spooka static int
xread_tfile(const char * mp,const char * path)42b23eef38Spooka xread_tfile(const char *mp, const char *path)
43b23eef38Spooka {
44b23eef38Spooka 	char pathb[MAXPATHLEN];
45b23eef38Spooka 	char buf[128];
46b23eef38Spooka 	int fd;
47b23eef38Spooka 
48b23eef38Spooka 	strcpy(pathb, mp);
49b23eef38Spooka 	strcat(pathb, "/");
50b23eef38Spooka 	strcat(pathb, path);
51b23eef38Spooka 
52b23eef38Spooka 	fd = rump_sys_open(pathb, O_RDONLY);
53b23eef38Spooka 	if (fd == -1)
54b23eef38Spooka 		return errno;
55b23eef38Spooka 	if (rump_sys_read(fd, buf, sizeof(buf)) == -1)
56b23eef38Spooka 		atf_tc_fail_errno("read tfile");
57b23eef38Spooka 	RL(rump_sys_close(fd));
58b23eef38Spooka 	if (strcmp(buf, MSTR) == 0)
59b23eef38Spooka 		return 0;
60b23eef38Spooka 	return EPROGMISMATCH;
61b23eef38Spooka }
62b23eef38Spooka 
6343c818f7Spooka /*
6443c818f7Spooka  * Mount a unionfs for testing.  Before calling, "mp" contains
6543c818f7Spooka  * the upper layer.  Lowerpath is constructed so that the directory
6643c818f7Spooka  * contains rumpfs.
6743c818f7Spooka  */
68b23eef38Spooka static void
mountunion(const char * mp,char * lowerpath)6943c818f7Spooka mountunion(const char *mp, char *lowerpath)
70b23eef38Spooka {
71b23eef38Spooka 	struct union_args unionargs;
72b23eef38Spooka 
7343c818f7Spooka 	sprintf(lowerpath, "/lower");
7443c818f7Spooka 	rump_sys_mkdir(lowerpath, 0777);
75b23eef38Spooka 
76b23eef38Spooka 	/* mount the union with our testfs as the upper layer */
77b23eef38Spooka 	memset(&unionargs, 0, sizeof(unionargs));
78b23eef38Spooka 	unionargs.target = lowerpath;
79b23eef38Spooka 	unionargs.mntflags = UNMNT_BELOW;
80b23eef38Spooka 
81b23eef38Spooka 	if (rump_sys_mount(MOUNT_UNION, mp, 0,
8265d8e9ccSpooka 	    &unionargs, sizeof(unionargs)) == -1) {
8365d8e9ccSpooka 		if (errno == EOPNOTSUPP) {
8465d8e9ccSpooka 			atf_tc_skip("fs does not support VOP_WHITEOUT");
8565d8e9ccSpooka 		} else {
86b23eef38Spooka 			atf_tc_fail_errno("union mount");
8765d8e9ccSpooka 		}
8865d8e9ccSpooka 	}
8943c818f7Spooka }
9043c818f7Spooka 
9143c818f7Spooka #if 0
9243c818f7Spooka static void
9343c818f7Spooka toggleroot(void)
9443c818f7Spooka {
9543c818f7Spooka 	static int status;
9643c818f7Spooka 
9743c818f7Spooka 	status ^= MNT_RDONLY;
9843c818f7Spooka 
9943c818f7Spooka 	printf("0x%x\n", status);
10043c818f7Spooka 	RL(rump_sys_mount(MOUNT_RUMPFS, "/", status | MNT_UPDATE, NULL, 0));
10143c818f7Spooka }
10243c818f7Spooka #endif
10343c818f7Spooka 
10443c818f7Spooka #define TFILE "tensti"
10543c818f7Spooka #define TDIR "testdir"
10643c818f7Spooka #define TDFILE TDIR "/indir"
10743c818f7Spooka 
10843c818f7Spooka static void
basic(const atf_tc_t * tc,const char * mp)10943c818f7Spooka basic(const atf_tc_t *tc, const char *mp)
11043c818f7Spooka {
11143c818f7Spooka 	char lowerpath[MAXPATHLEN];
11243c818f7Spooka 	char dbuf[8192];
11343c818f7Spooka 	struct stat sb;
11443c818f7Spooka 	struct dirent *dp;
11543c818f7Spooka 	int error, fd, dsize;
11643c818f7Spooka 
11743c818f7Spooka 	mountunion(mp, lowerpath);
11843c818f7Spooka 
11943c818f7Spooka 	/* create a file in the lower layer */
12043c818f7Spooka 	xput_tfile(lowerpath, TFILE);
121b23eef38Spooka 
122b23eef38Spooka 	/* first, test we can read the old file from the new namespace */
123b23eef38Spooka 	error = xread_tfile(mp, TFILE);
124b23eef38Spooka 	if (error != 0)
125b23eef38Spooka 		atf_tc_fail("union compare failed: %d (%s)",
126b23eef38Spooka 		    error, strerror(error));
127b23eef38Spooka 
128b23eef38Spooka 	/* then, test upper layer writes don't affect the lower layer */
129b23eef38Spooka 	xput_tfile(mp, "kiekko");
130b23eef38Spooka 	if ((error = xread_tfile(lowerpath, "kiekko")) != ENOENT)
131b23eef38Spooka 		atf_tc_fail("invisibility failed: %d (%s)",
132b23eef38Spooka 		    error, strerror(error));
133b23eef38Spooka 
134b23eef38Spooka 	/* check that we can whiteout stuff in the upper layer */
135b23eef38Spooka 	FSTEST_ENTER();
136b23eef38Spooka 	RL(rump_sys_unlink(TFILE));
137b23eef38Spooka 	ATF_REQUIRE_ERRNO(ENOENT, rump_sys_stat(TFILE, &sb) == -1);
138b23eef38Spooka 	FSTEST_EXIT();
139b23eef38Spooka 
140b23eef38Spooka 	/* check that the removed node is not in the directory listing */
141b23eef38Spooka 	RL(fd = rump_sys_open(mp, O_RDONLY));
142b23eef38Spooka 	RL(dsize = rump_sys_getdents(fd, dbuf, sizeof(dbuf)));
143b23eef38Spooka 	for (dp = (struct dirent *)dbuf;
144b23eef38Spooka 	    (char *)dp < dbuf + dsize;
145b23eef38Spooka 	    dp = _DIRENT_NEXT(dp)) {
146b23eef38Spooka 		if (strcmp(dp->d_name, TFILE) == 0 && dp->d_type != DT_WHT)
147b23eef38Spooka 			atf_tc_fail("removed file non-white-outed");
148b23eef38Spooka 	}
149b23eef38Spooka 	RL(rump_sys_close(fd));
150b23eef38Spooka 
151b23eef38Spooka 	RL(rump_sys_unmount(mp, 0));
152b23eef38Spooka }
153b23eef38Spooka 
15443c818f7Spooka static void
whiteout(const atf_tc_t * tc,const char * mp)15543c818f7Spooka whiteout(const atf_tc_t *tc, const char *mp)
15643c818f7Spooka {
15743c818f7Spooka 	char lower[MAXPATHLEN];
15843c818f7Spooka 	struct stat sb;
15943c818f7Spooka 	void *fsarg;
16043c818f7Spooka 
16143c818f7Spooka 	/*
16243c818f7Spooka 	 * XXX: use ffs here to make sure any screwups in rumpfs don't
16343c818f7Spooka 	 * affect the test
16443c818f7Spooka 	 */
16543c818f7Spooka 	RL(ffs_fstest_newfs(tc, &fsarg, "daimage", 1024*1024*5, NULL));
16643c818f7Spooka 	RL(ffs_fstest_mount(tc, fsarg, "/lower", 0));
16743c818f7Spooka 
16843c818f7Spooka 	/* create a file in the lower layer */
16943c818f7Spooka 	RL(rump_sys_chdir("/lower"));
17043c818f7Spooka 	RL(rump_sys_mkdir(TDIR, 0777));
17143c818f7Spooka 	RL(rump_sys_mkdir(TDFILE, 0777));
17243c818f7Spooka 	RL(rump_sys_chdir("/"));
17343c818f7Spooka 
17443c818f7Spooka 	RL(ffs_fstest_unmount(tc, "/lower", 0));
17543c818f7Spooka 	RL(ffs_fstest_mount(tc, fsarg, "/lower", MNT_RDONLY));
17643c818f7Spooka 
17743c818f7Spooka 	mountunion(mp, lower);
17843c818f7Spooka 
17943c818f7Spooka 	FSTEST_ENTER();
180342315ffShannken 	ATF_REQUIRE_ERRNO(ENOTEMPTY, rump_sys_rmdir(TDIR) == -1);
181342315ffShannken 	RL(rump_sys_rmdir(TDFILE));
18243c818f7Spooka 	RL(rump_sys_rmdir(TDIR));
18343c818f7Spooka 	ATF_REQUIRE_ERRNO(ENOENT, rump_sys_stat(TDFILE, &sb) == -1);
18443c818f7Spooka 	ATF_REQUIRE_ERRNO(ENOENT, rump_sys_stat(TDIR, &sb) == -1);
18543c818f7Spooka 
18643c818f7Spooka 	RL(rump_sys_mkdir(TDIR, 0777));
18743c818f7Spooka 	RL(rump_sys_stat(TDIR, &sb));
18843c818f7Spooka 	ATF_REQUIRE_ERRNO(ENOENT, rump_sys_stat(TDFILE, &sb) == -1);
18943c818f7Spooka 	FSTEST_EXIT();
19043c818f7Spooka 
19143c818f7Spooka 	RL(rump_sys_unmount(mp, 0));
19243c818f7Spooka }
19343c818f7Spooka 
194b23eef38Spooka ATF_TC_FSAPPLY(basic, "check basic union functionality");
19543c818f7Spooka ATF_TC_FSAPPLY(whiteout, "create whiteout in upper layer");
196b23eef38Spooka 
ATF_TP_ADD_TCS(tp)197b23eef38Spooka ATF_TP_ADD_TCS(tp)
198b23eef38Spooka {
199b23eef38Spooka 
200b23eef38Spooka 	ATF_TP_FSAPPLY(basic);
20143c818f7Spooka 	ATF_TP_FSAPPLY(whiteout);
20243c818f7Spooka 
203b23eef38Spooka 	return atf_no_error();
204b23eef38Spooka }
205