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