Home | History | Annotate | Line # | Download | only in vfs
      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 
     25 static void
     26 xput_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 
     41 static int
     42 xread_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  */
     68 static void
     69 mountunion(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
     92 static void
     93 toggleroot(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 
    108 static void
    109 basic(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 
    154 static void
    155 whiteout(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 
    194 ATF_TC_FSAPPLY(basic, "check basic union functionality");
    195 ATF_TC_FSAPPLY(whiteout, "create whiteout in upper layer");
    196 
    197 ATF_TP_ADD_TCS(tp)
    198 {
    199 
    200 	ATF_TP_FSAPPLY(basic);
    201 	ATF_TP_FSAPPLY(whiteout);
    202 
    203 	return atf_no_error();
    204 }
    205