1*63d1fd59SEnji Cooper /* $NetBSD: t_union.c,v 1.9 2017/01/13 21:30:40 christos Exp $ */ 257718be8SEnji Cooper 357718be8SEnji Cooper #include <sys/types.h> 457718be8SEnji Cooper #include <sys/mount.h> 557718be8SEnji Cooper 657718be8SEnji Cooper #include <atf-c.h> 757718be8SEnji Cooper #include <err.h> 857718be8SEnji Cooper #include <errno.h> 957718be8SEnji Cooper #include <fcntl.h> 1057718be8SEnji Cooper #include <stdio.h> 1157718be8SEnji Cooper #include <unistd.h> 1257718be8SEnji Cooper #include <string.h> 1357718be8SEnji Cooper #include <stdlib.h> 1457718be8SEnji Cooper 1557718be8SEnji Cooper #include <rump/rump.h> 1657718be8SEnji Cooper #include <rump/rump_syscalls.h> 1757718be8SEnji Cooper 1857718be8SEnji Cooper #include <miscfs/union/union.h> 1957718be8SEnji Cooper 20*63d1fd59SEnji Cooper #include "h_macros.h" 2157718be8SEnji Cooper #include "../common/h_fsmacros.h" 2257718be8SEnji Cooper 2357718be8SEnji Cooper #define MSTR "magic bus" 2457718be8SEnji Cooper 2557718be8SEnji Cooper static void 2657718be8SEnji Cooper xput_tfile(const char *mp, const char *path) 2757718be8SEnji Cooper { 2857718be8SEnji Cooper char pathb[MAXPATHLEN]; 2957718be8SEnji Cooper int fd; 3057718be8SEnji Cooper 3157718be8SEnji Cooper strcpy(pathb, mp); 3257718be8SEnji Cooper strcat(pathb, "/"); 3357718be8SEnji Cooper strcat(pathb, path); 3457718be8SEnji Cooper 3557718be8SEnji Cooper RL(fd = rump_sys_open(pathb, O_CREAT | O_RDWR, 0777)); 3657718be8SEnji Cooper if (rump_sys_write(fd, MSTR, sizeof(MSTR)) != sizeof(MSTR)) 3757718be8SEnji Cooper atf_tc_fail_errno("write to testfile"); 3857718be8SEnji Cooper RL(rump_sys_close(fd)); 3957718be8SEnji Cooper } 4057718be8SEnji Cooper 4157718be8SEnji Cooper static int 4257718be8SEnji Cooper xread_tfile(const char *mp, const char *path) 4357718be8SEnji Cooper { 4457718be8SEnji Cooper char pathb[MAXPATHLEN]; 4557718be8SEnji Cooper char buf[128]; 4657718be8SEnji Cooper int fd; 4757718be8SEnji Cooper 4857718be8SEnji Cooper strcpy(pathb, mp); 4957718be8SEnji Cooper strcat(pathb, "/"); 5057718be8SEnji Cooper strcat(pathb, path); 5157718be8SEnji Cooper 5257718be8SEnji Cooper fd = rump_sys_open(pathb, O_RDONLY); 5357718be8SEnji Cooper if (fd == -1) 5457718be8SEnji Cooper return errno; 5557718be8SEnji Cooper if (rump_sys_read(fd, buf, sizeof(buf)) == -1) 5657718be8SEnji Cooper atf_tc_fail_errno("read tfile"); 5757718be8SEnji Cooper RL(rump_sys_close(fd)); 5857718be8SEnji Cooper if (strcmp(buf, MSTR) == 0) 5957718be8SEnji Cooper return 0; 6057718be8SEnji Cooper return EPROGMISMATCH; 6157718be8SEnji Cooper } 6257718be8SEnji Cooper 6357718be8SEnji Cooper /* 6457718be8SEnji Cooper * Mount a unionfs for testing. Before calling, "mp" contains 6557718be8SEnji Cooper * the upper layer. Lowerpath is constructed so that the directory 6657718be8SEnji Cooper * contains rumpfs. 6757718be8SEnji Cooper */ 6857718be8SEnji Cooper static void 6957718be8SEnji Cooper mountunion(const char *mp, char *lowerpath) 7057718be8SEnji Cooper { 7157718be8SEnji Cooper struct union_args unionargs; 7257718be8SEnji Cooper 7357718be8SEnji Cooper sprintf(lowerpath, "/lower"); 7457718be8SEnji Cooper rump_sys_mkdir(lowerpath, 0777); 7557718be8SEnji Cooper 7657718be8SEnji Cooper /* mount the union with our testfs as the upper layer */ 7757718be8SEnji Cooper memset(&unionargs, 0, sizeof(unionargs)); 7857718be8SEnji Cooper unionargs.target = lowerpath; 7957718be8SEnji Cooper unionargs.mntflags = UNMNT_BELOW; 8057718be8SEnji Cooper 8157718be8SEnji Cooper if (rump_sys_mount(MOUNT_UNION, mp, 0, 8257718be8SEnji Cooper &unionargs, sizeof(unionargs)) == -1) { 8357718be8SEnji Cooper if (errno == EOPNOTSUPP) { 8457718be8SEnji Cooper atf_tc_skip("fs does not support VOP_WHITEOUT"); 8557718be8SEnji Cooper } else { 8657718be8SEnji Cooper atf_tc_fail_errno("union mount"); 8757718be8SEnji Cooper } 8857718be8SEnji Cooper } 8957718be8SEnji Cooper } 9057718be8SEnji Cooper 9157718be8SEnji Cooper #if 0 9257718be8SEnji Cooper static void 9357718be8SEnji Cooper toggleroot(void) 9457718be8SEnji Cooper { 9557718be8SEnji Cooper static int status; 9657718be8SEnji Cooper 9757718be8SEnji Cooper status ^= MNT_RDONLY; 9857718be8SEnji Cooper 9957718be8SEnji Cooper printf("0x%x\n", status); 10057718be8SEnji Cooper RL(rump_sys_mount(MOUNT_RUMPFS, "/", status | MNT_UPDATE, NULL, 0)); 10157718be8SEnji Cooper } 10257718be8SEnji Cooper #endif 10357718be8SEnji Cooper 10457718be8SEnji Cooper #define TFILE "tensti" 10557718be8SEnji Cooper #define TDIR "testdir" 10657718be8SEnji Cooper #define TDFILE TDIR "/indir" 10757718be8SEnji Cooper 10857718be8SEnji Cooper static void 10957718be8SEnji Cooper basic(const atf_tc_t *tc, const char *mp) 11057718be8SEnji Cooper { 11157718be8SEnji Cooper char lowerpath[MAXPATHLEN]; 11257718be8SEnji Cooper char dbuf[8192]; 11357718be8SEnji Cooper struct stat sb; 11457718be8SEnji Cooper struct dirent *dp; 11557718be8SEnji Cooper int error, fd, dsize; 11657718be8SEnji Cooper 11757718be8SEnji Cooper mountunion(mp, lowerpath); 11857718be8SEnji Cooper 11957718be8SEnji Cooper /* create a file in the lower layer */ 12057718be8SEnji Cooper xput_tfile(lowerpath, TFILE); 12157718be8SEnji Cooper 12257718be8SEnji Cooper /* first, test we can read the old file from the new namespace */ 12357718be8SEnji Cooper error = xread_tfile(mp, TFILE); 12457718be8SEnji Cooper if (error != 0) 12557718be8SEnji Cooper atf_tc_fail("union compare failed: %d (%s)", 12657718be8SEnji Cooper error, strerror(error)); 12757718be8SEnji Cooper 12857718be8SEnji Cooper /* then, test upper layer writes don't affect the lower layer */ 12957718be8SEnji Cooper xput_tfile(mp, "kiekko"); 13057718be8SEnji Cooper if ((error = xread_tfile(lowerpath, "kiekko")) != ENOENT) 13157718be8SEnji Cooper atf_tc_fail("invisibility failed: %d (%s)", 13257718be8SEnji Cooper error, strerror(error)); 13357718be8SEnji Cooper 13457718be8SEnji Cooper /* check that we can whiteout stuff in the upper layer */ 13557718be8SEnji Cooper FSTEST_ENTER(); 13657718be8SEnji Cooper RL(rump_sys_unlink(TFILE)); 13757718be8SEnji Cooper ATF_REQUIRE_ERRNO(ENOENT, rump_sys_stat(TFILE, &sb) == -1); 13857718be8SEnji Cooper FSTEST_EXIT(); 13957718be8SEnji Cooper 14057718be8SEnji Cooper /* check that the removed node is not in the directory listing */ 14157718be8SEnji Cooper RL(fd = rump_sys_open(mp, O_RDONLY)); 14257718be8SEnji Cooper RL(dsize = rump_sys_getdents(fd, dbuf, sizeof(dbuf))); 14357718be8SEnji Cooper for (dp = (struct dirent *)dbuf; 14457718be8SEnji Cooper (char *)dp < dbuf + dsize; 14557718be8SEnji Cooper dp = _DIRENT_NEXT(dp)) { 14657718be8SEnji Cooper if (strcmp(dp->d_name, TFILE) == 0 && dp->d_type != DT_WHT) 14757718be8SEnji Cooper atf_tc_fail("removed file non-white-outed"); 14857718be8SEnji Cooper } 14957718be8SEnji Cooper RL(rump_sys_close(fd)); 15057718be8SEnji Cooper 15157718be8SEnji Cooper RL(rump_sys_unmount(mp, 0)); 15257718be8SEnji Cooper } 15357718be8SEnji Cooper 15457718be8SEnji Cooper static void 15557718be8SEnji Cooper whiteout(const atf_tc_t *tc, const char *mp) 15657718be8SEnji Cooper { 15757718be8SEnji Cooper char lower[MAXPATHLEN]; 15857718be8SEnji Cooper struct stat sb; 15957718be8SEnji Cooper void *fsarg; 16057718be8SEnji Cooper 16157718be8SEnji Cooper /* 16257718be8SEnji Cooper * XXX: use ffs here to make sure any screwups in rumpfs don't 16357718be8SEnji Cooper * affect the test 16457718be8SEnji Cooper */ 16557718be8SEnji Cooper RL(ffs_fstest_newfs(tc, &fsarg, "daimage", 1024*1024*5, NULL)); 16657718be8SEnji Cooper RL(ffs_fstest_mount(tc, fsarg, "/lower", 0)); 16757718be8SEnji Cooper 16857718be8SEnji Cooper /* create a file in the lower layer */ 16957718be8SEnji Cooper RL(rump_sys_chdir("/lower")); 17057718be8SEnji Cooper RL(rump_sys_mkdir(TDIR, 0777)); 17157718be8SEnji Cooper RL(rump_sys_mkdir(TDFILE, 0777)); 17257718be8SEnji Cooper RL(rump_sys_chdir("/")); 17357718be8SEnji Cooper 17457718be8SEnji Cooper RL(ffs_fstest_unmount(tc, "/lower", 0)); 17557718be8SEnji Cooper RL(ffs_fstest_mount(tc, fsarg, "/lower", MNT_RDONLY)); 17657718be8SEnji Cooper 17757718be8SEnji Cooper mountunion(mp, lower); 17857718be8SEnji Cooper 17957718be8SEnji Cooper FSTEST_ENTER(); 18057718be8SEnji Cooper ATF_REQUIRE_ERRNO(ENOTEMPTY, rump_sys_rmdir(TDIR) == -1); 18157718be8SEnji Cooper RL(rump_sys_rmdir(TDFILE)); 18257718be8SEnji Cooper RL(rump_sys_rmdir(TDIR)); 18357718be8SEnji Cooper ATF_REQUIRE_ERRNO(ENOENT, rump_sys_stat(TDFILE, &sb) == -1); 18457718be8SEnji Cooper ATF_REQUIRE_ERRNO(ENOENT, rump_sys_stat(TDIR, &sb) == -1); 18557718be8SEnji Cooper 18657718be8SEnji Cooper RL(rump_sys_mkdir(TDIR, 0777)); 18757718be8SEnji Cooper RL(rump_sys_stat(TDIR, &sb)); 18857718be8SEnji Cooper ATF_REQUIRE_ERRNO(ENOENT, rump_sys_stat(TDFILE, &sb) == -1); 18957718be8SEnji Cooper FSTEST_EXIT(); 19057718be8SEnji Cooper 19157718be8SEnji Cooper RL(rump_sys_unmount(mp, 0)); 19257718be8SEnji Cooper } 19357718be8SEnji Cooper 19457718be8SEnji Cooper ATF_TC_FSAPPLY(basic, "check basic union functionality"); 19557718be8SEnji Cooper ATF_TC_FSAPPLY(whiteout, "create whiteout in upper layer"); 19657718be8SEnji Cooper 19757718be8SEnji Cooper ATF_TP_ADD_TCS(tp) 19857718be8SEnji Cooper { 19957718be8SEnji Cooper 20057718be8SEnji Cooper ATF_TP_FSAPPLY(basic); 20157718be8SEnji Cooper ATF_TP_FSAPPLY(whiteout); 20257718be8SEnji Cooper 20357718be8SEnji Cooper return atf_no_error(); 20457718be8SEnji Cooper } 205