aboutsummaryrefslogtreecommitdiff
path: root/contrib/netbsd-tests/fs
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/netbsd-tests/fs')
-rw-r--r--contrib/netbsd-tests/fs/cd9660/pr_48787.image.bz2.uue103
-rwxr-xr-xcontrib/netbsd-tests/fs/cd9660/t_high_ino_big_file.sh118
-rw-r--r--contrib/netbsd-tests/fs/common/fstest_ext2fs.c139
-rw-r--r--contrib/netbsd-tests/fs/common/fstest_ffs.c156
-rw-r--r--contrib/netbsd-tests/fs/common/fstest_lfs.c190
-rw-r--r--contrib/netbsd-tests/fs/common/fstest_msdosfs.c140
-rw-r--r--contrib/netbsd-tests/fs/common/fstest_nfs.c326
-rw-r--r--contrib/netbsd-tests/fs/common/fstest_puffs.c452
-rw-r--r--contrib/netbsd-tests/fs/common/fstest_rumpfs.c90
-rw-r--r--contrib/netbsd-tests/fs/common/fstest_sysvbfs.c139
-rw-r--r--contrib/netbsd-tests/fs/common/fstest_tmpfs.c112
-rw-r--r--contrib/netbsd-tests/fs/common/fstest_udf.c153
-rw-r--r--contrib/netbsd-tests/fs/common/fstest_v7fs.c140
-rw-r--r--contrib/netbsd-tests/fs/common/fstest_zfs.c134
-rw-r--r--contrib/netbsd-tests/fs/common/h_fsmacros.h329
-rw-r--r--contrib/netbsd-tests/fs/common/snapshot.c228
-rwxr-xr-xcontrib/netbsd-tests/fs/ffs/ffs_common.sh99
-rw-r--r--contrib/netbsd-tests/fs/ffs/h_ffs_server.c113
-rw-r--r--contrib/netbsd-tests/fs/ffs/h_quota2_tests.c468
-rwxr-xr-xcontrib/netbsd-tests/fs/ffs/quotas_common.sh12
-rwxr-xr-xcontrib/netbsd-tests/fs/ffs/t_clearquota.sh91
-rw-r--r--contrib/netbsd-tests/fs/ffs/t_fifos.c158
-rwxr-xr-xcontrib/netbsd-tests/fs/ffs/t_getquota.sh112
-rwxr-xr-xcontrib/netbsd-tests/fs/ffs/t_miscquota.sh213
-rw-r--r--contrib/netbsd-tests/fs/ffs/t_mount.c138
-rw-r--r--contrib/netbsd-tests/fs/ffs/t_quota2_1.c114
-rw-r--r--contrib/netbsd-tests/fs/ffs/t_quota2_remount.c139
-rwxr-xr-xcontrib/netbsd-tests/fs/ffs/t_quotalimit.sh345
-rwxr-xr-xcontrib/netbsd-tests/fs/ffs/t_setquota.sh203
-rw-r--r--contrib/netbsd-tests/fs/ffs/t_snapshot.c43
-rw-r--r--contrib/netbsd-tests/fs/ffs/t_snapshot_log.c46
-rw-r--r--contrib/netbsd-tests/fs/ffs/t_snapshot_v2.c43
-rw-r--r--contrib/netbsd-tests/fs/fifofs/t_fifo.c238
-rw-r--r--contrib/netbsd-tests/fs/h_funcs.subr75
-rw-r--r--contrib/netbsd-tests/fs/hfs/colon.hfs.bz2.uue35
-rw-r--r--contrib/netbsd-tests/fs/hfs/t_pathconvert.c83
-rw-r--r--contrib/netbsd-tests/fs/kernfs/t_basic.c133
-rw-r--r--contrib/netbsd-tests/fs/lfs/t_pr.c60
-rw-r--r--contrib/netbsd-tests/fs/msdosfs/t_snapshot.c51
-rw-r--r--contrib/netbsd-tests/fs/nfs/nfsservice/README16
-rw-r--r--contrib/netbsd-tests/fs/nfs/nfsservice/exports12
-rw-r--r--contrib/netbsd-tests/fs/nfs/nfsservice/getmntinfo.c85
-rw-r--r--contrib/netbsd-tests/fs/nfs/nfsservice/pathnames.h37
-rw-r--r--contrib/netbsd-tests/fs/nfs/nfsservice/rumpnfsd.c166
-rw-r--r--contrib/netbsd-tests/fs/nfs/t_mountd.c121
-rwxr-xr-xcontrib/netbsd-tests/fs/nfs/t_rquotad.sh142
-rw-r--r--contrib/netbsd-tests/fs/nullfs/t_basic.c174
-rw-r--r--contrib/netbsd-tests/fs/psshfs/h_have_puffs.c58
-rw-r--r--contrib/netbsd-tests/fs/psshfs/ssh_config.in14
-rw-r--r--contrib/netbsd-tests/fs/psshfs/ssh_host_key15
-rw-r--r--contrib/netbsd-tests/fs/psshfs/ssh_host_key.pub1
-rw-r--r--contrib/netbsd-tests/fs/psshfs/sshd_config.in40
-rwxr-xr-xcontrib/netbsd-tests/fs/psshfs/t_psshfs.sh295
-rw-r--r--contrib/netbsd-tests/fs/ptyfs/t_nullpts.c128
-rw-r--r--contrib/netbsd-tests/fs/ptyfs/t_ptyfs.c62
-rw-r--r--contrib/netbsd-tests/fs/puffs/h_dtfs/dtfs.c255
-rw-r--r--contrib/netbsd-tests/fs/puffs/h_dtfs/dtfs.h127
-rw-r--r--contrib/netbsd-tests/fs/puffs/h_dtfs/dtfs_subr.c358
-rw-r--r--contrib/netbsd-tests/fs/puffs/h_dtfs/dtfs_vfsops.c298
-rw-r--r--contrib/netbsd-tests/fs/puffs/h_dtfs/dtfs_vnops.c586
-rw-r--r--contrib/netbsd-tests/fs/puffs/t_basic.c455
-rw-r--r--contrib/netbsd-tests/fs/puffs/t_fuzz.c283
-rw-r--r--contrib/netbsd-tests/fs/puffs/t_io.c61
-rw-r--r--contrib/netbsd-tests/fs/tmpfs/README17
-rw-r--r--contrib/netbsd-tests/fs/tmpfs/h_funcs.subr115
-rw-r--r--contrib/netbsd-tests/fs/tmpfs/h_tools.c321
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_create.sh122
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_devices.sh60
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_dots.sh67
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_exec.sh52
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_link.sh144
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_mkdir.sh159
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_mknod.sh143
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_mount.sh156
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_pipes.sh52
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_read_write.sh87
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_readdir.sh116
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_remove.sh125
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_rename.sh278
-rw-r--r--contrib/netbsd-tests/fs/tmpfs/t_renamerace.c115
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_rmdir.sh204
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_setattr.sh216
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_sizes.sh139
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_sockets.sh52
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_statvfs.sh67
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_symlink.sh114
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_times.sh169
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_trail_slash.sh52
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_truncate.sh56
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_vnd.sh102
-rwxr-xr-xcontrib/netbsd-tests/fs/tmpfs/t_vnode_leak.sh68
-rw-r--r--contrib/netbsd-tests/fs/umapfs/t_basic.c145
-rw-r--r--contrib/netbsd-tests/fs/union/t_pr.c130
-rw-r--r--contrib/netbsd-tests/fs/vfs/t_full.c101
-rw-r--r--contrib/netbsd-tests/fs/vfs/t_io.c268
-rw-r--r--contrib/netbsd-tests/fs/vfs/t_mtime_otrunc.c86
-rw-r--r--contrib/netbsd-tests/fs/vfs/t_renamerace.c190
-rw-r--r--contrib/netbsd-tests/fs/vfs/t_rmdirrace.c106
-rw-r--r--contrib/netbsd-tests/fs/vfs/t_ro.c203
-rw-r--r--contrib/netbsd-tests/fs/vfs/t_rwtoro.c233
-rw-r--r--contrib/netbsd-tests/fs/vfs/t_union.c204
-rw-r--r--contrib/netbsd-tests/fs/vfs/t_unpriv.c239
-rw-r--r--contrib/netbsd-tests/fs/vfs/t_vfsops.c211
-rw-r--r--contrib/netbsd-tests/fs/vfs/t_vnops.c1080
-rwxr-xr-xcontrib/netbsd-tests/fs/zfs/t_zpool.sh66
105 files changed, 16380 insertions, 0 deletions
diff --git a/contrib/netbsd-tests/fs/cd9660/pr_48787.image.bz2.uue b/contrib/netbsd-tests/fs/cd9660/pr_48787.image.bz2.uue
new file mode 100644
index 000000000000..d8f7488f36d2
--- /dev/null
+++ b/contrib/netbsd-tests/fs/cd9660/pr_48787.image.bz2.uue
@@ -0,0 +1,103 @@
+begin 644 pr_48787.image.bz2
+M0EIH.3%!62936>D^3`0``+M_V?_7U17T!W_H/^_?8..V$"1DB`0``B$`0I-H
+MP`+N-!HLI8)131H4](]3TC(VH`S4;4````T&@R&C0R:!*$1D(8B:C!,(Q-&3
+M-`!,``"&:`FC'&AH&C3(TT:9`8F"``&@-`:9`8$R`J1(A&A$R&391FA#TC0V
+MHQ'J8@&T@VHT`QII&Z!"B5;S7YT+1"ZB8Y+5"M,5_J`!]Y:[$2%,%W`ZAA2@
+MVE)'DK^*?'3QU`DFQ4@22)(B[N3GZN?E<)`DDB.C)7\X71*B1K*2))(DD1HI
+MU5$TUVF[4JP89^FHY$B*FEBC(RN$5"8O'XO]1++(._=;ZKXR%:QK3^W7/>RT
+M8EODZ#MX53U8C0F]6O.G4J/9YNQA$B2))$<[<CT.C.)G[#NK)A3#\KM]#AP,
+M*V*=Z35CIY9O/Q-6T7&8=_%R-%>H5(]"2=])+_3RW)O*AI[)<ZVSDZ"I-3B2
+M3;T^%)3LM2^.3I+<>9@D^JV5RK3?43-;"G;69#AH8,(5;:7UN,<5%98$9;9;
+M\G*RT*37_9[;D'P/`1(R-$D.!36\S6N:_M>!][4*&3?'S858VCD+-3PINFR8
+M,C9?J+9ME:9.,_OR\C;3K'27W:K<<&>MMC;<YN*;!H83)Y-+"BCM6:&H"OT2
+M-]XWT/ZSW69R8]UG%S8IV5)$%2X)\%PF)G>HS".>68$Y&.B608>JM?)+3$:Z
+MY539A<7-E*'_YY..1'ECZBIG5%1%U93-9*1-5T_C!\)&2@?*CQB>(Q\0G(J<
+M6%PDB4L,2$>Q4IM%6A+%4K/0-!7*348,9!&#<9@JNP&+LBI>+9&*JC6ZE293
+MY%-8H^HI12#5$9J;"0@$DA.](1+1.SH#B10&H6C48:PMPI7=#M2A@I%'\J2P
+M.Y(+0C:B.&>F(^[(>)1+M\+QH%@O#4OE(\RE*14IZ7+9R*G8IYV^O=Y<=4HY
+M1C@SCIZ=$<UY5X+M6FT2)(DD1O#AYE/_]9B@K),IK(<$\6^`K\<@`&````00
+M`!A`)J,A4!+4A4!+F*"LDRFLAP3Q;X"OQR``8```!!``&$`FHR%0$M2%0$N8
+MH*R3*:R'!/%O@*_'(`!@```$$``80":C(5`2U(5`2YB@K),IK(<$\6^`K\<@
+M`&````00`!A`)J,A4!+4A4!+F*"LDRFLAP3Q;X"OQR``8```!!``&$`FHR%0
+M$M2%0$N8H*R3*:R'!/%O@*_'(`!@```$$``80":C(5`2U(5`2YB@K),IK(<$
+M\6^`K\<@`&````00`!A`)J,A4!+4A4!+F*"LDRFLAP3Q;X"OQR``8```!!``
+M&$`FHR%0$M2%0$N8H*R3*:R'!/%O@*_'(`!@```$$``80":C(5`2U(5`2YB@
+MK),IK(<$\6^`K\<@`&````00`!A`)J,A4!+4A4!+F*"LDRFLAP3Q;X"OQR``
+M8```!!``&$`FHR%0$M2%0$N8H*R3*:R'!/%O@*_'(`!@```$$``80":C(5`2
+MU(5`2YB@K),IK(<$\6^`K\<@`&````00`!A`)J,A4!+4A4!+F*"LDRFLAP3Q
+M;X"OQR``8```!!``&$`FHR%0$M2%0$N8H*R3*:R'!/%O@*_'(`!@```$$``8
+M0":C(5`2U(5`2YB@K),IK(<$\6^`K\<@`&````00`!A`)J,A4!+4A4!+F*"L
+MDRFLAP3Q;X"OQR``8```!!``&$`FHR%0$M2%0$N8H*R3*:R'!/%O@*_'(`!@
+M```$$``80":C(5`2U(5`2YB@K),IK(<$\6^`K\<@`&````00`!A`)J,A4!+4
+MA4!+F*"LDRFLAP3Q;X"OQR``8```!!``&$`FHR%0$M2%0$N8H*R3*:R'!/%O
+M@*_'(`!@```$$``80":C(5`2U(5`2YB@K),IK(<$\6^`K\<@`&````00`!A`
+M)J,A4!+4A4!+F*"LDRFLAP3Q;X"OQR``8```!!``&$`FHR%0$M2%0$N8H*R3
+M*:R'!/%O@*_'(`!@```$$``80":C(5`2U(5`2YB@K),IK(<$\6^`K\<@`&``
+M``00`!A`)J,A4!+4A4!+F*"LDRFLAP3Q;X"OQR``8```!!``&$`FHR%0$M2%
+M0$N8H*R3*:R'!/%O@*_'(`!@```$$``80":C(5`2U(5`2YB@K),IK(<$\6^`
+MK\<@`&````00`!A`)J,A4!+4A4!+F*"LDRFLAP3Q;X"OQR``8```!!``&$`F
+MHR%0$M2%0$N8H*R3*:R'!/%O@*_'(`!@```$$``80":C(5`2U(5`2YB@K),I
+MK(<$\6^`K\<@`&````00`!A`)J,A4!+4A4!+F*"LDRFLAP3Q;X"OQR``8```
+M!!``&$`FHR%0$M2%0$N8H*R3*:R'!/%O@*_'(`!@```$$``80":C(5`2U(5`
+M2YB@K),IK(<$\6^`K\<@`&````00`!A`)J,A4!+4A4!+F*"LDRFLAP3Q;X"O
+MQR``8```!!``&$`FHR%0$M2%0$N8H*R3*:R'!/%O@*_'(`!@```$$``80":C
+M(5`2U(5`2YB@K),IK(<$\6^`K\<@`&````00`!A`)J,A4!+4A4!+F*"LDRFL
+MAP3Q;X"OQR``8```!!``&$`FHR%0$M2%0$N8H*R3*:R'!/%O@*_'(`!@```$
+M$``80":C(5`2U(5`2YB@K),IK(<$\6^`K\<@`&````00`!A`)J,A4!+4A4!+
+MF*"LDRFLAP3Q;X"OQR``8```!!``&$`FHR%0$M2%0$N8H*R3*:R'!/%O@*_'
+M(`!@```$$``80":C(5`2U(5`2YB@K),IK(<$\6^`K\<@`&````00`!A`)J,A
+M4!+4A4!+F*"LDRFLAP3Q;X"OQR``8```!!``&$`FHR%0$M2%0$N8H*R3*:R'
+M!/%O@*_'(`!@```$$``80":C(5`2U(5`2YB@K),IK(<$\6^`K\<@`&````00
+M`!A`)J,A4!+4A4!+F*"LDRFLAP3Q;X"OQR``8```!!``&$`FHR%0$M2%0$N8
+MH*R3*:R'!/%O@*_'(`!@```$$``80":C(5`2U(5`2YB@K),IK(<$\6^`K\<@
+M`&````00`!A`)J,A4!+4A4!+F*"LDRFLAP3Q;X"OQR``8```!!``&$`FHR%0
+M$M2%0$N8H*R3*:R'!/%O@*_'(`!@```$$``80":C(5`2U(5`2YB@K),IK(<$
+M\6^`K\<@`&````00`!A`)J,A4!+4A4!+F*"LDRFLAP3Q;X"OQR``8```!!``
+M&$`FHR%0$M2%0$N8H*R3*:R'!/%O@*_'(`!@```$$``80":C(5`2U(5`2YB@
+MK),IK(<$\6^`K\<@`&````00`!A`)J,A4!+4A4!+F*"LDRFLAP3Q;X"OQR``
+M8```!!``&$`FHR%0$M2%0$N8H*R3*:R'!/%O@*_'(`!@```$$``80":C(5`2
+MU(5`2YB@K),IK(<$\6^`K\<@`&````00`!A`)J,A4!+4A4!+F*"LDRFLAP3Q
+M;X"OQR``8```!!``&$`FHR%0$M2%0$N8H*R3*:R'!/%O@*_'(`!@```$$``8
+M0":C(5`2U(5`2YB@K),IK(<$\6^`K\<@`&````00`!A`)J,A4!+4A4!+F*"L
+MDRFLAP3Q;X"OQR``8```!!``&$`FHR%0$M2%0$N8H*R3*:R'!/%O@*_'(`!@
+M```$$``80":C(5`2U(5`2YB@K),IK(<$\6^`K\<@`&````00`!A`)J,A4!+4
+MA4!+F*"LDRFLAP3Q;X"OQR``8```!!``&$`FHR%0$M2%0$N8H*R3*:R'!/%O
+M@*_'(`!@```$$``80":C(5`2U(5`2YB@K),IK(<$\6^`K\<@`&````00`!A`
+M)J,A4!+4A4!+F*"LDRFLAP3Q;X"OQR``8```!!``&$`FHR%0$M2%0$N8H*R3
+M*:R'!/%O@*_'(`!@```$$``80":C(5`2U(5`2YB@K),IK(<$\6^`K\<@`&``
+M``00`!A`)J,A4!+4A4!+F*"LDRFLAP3Q;X"OQR``8```!!``&$`FHR%0$M2%
+M0$N8H*R3*:R'!/%O@*_'(`!@```$$``80":C(5`2U(5`2YB@K),IK(<$\6^`
+MK\<@`&````00`!A`)J,A4!+4A4!+F*"LDRFLAP3Q;X"OQR``8```!!``&$`F
+MHR%0$M2%0$N8H*R3*:R'!/%O@*_'(`!@```$$``80":C(5`2U(5`2YB@K),I
+MK(<$\6^`K\<@`&````00`!A`)J,A4!+4A4!+F*"LDRFLAP3Q;X"OQR``8```
+M!!``&$`FHR%0$M2%0$N8H*R3*:R'!/%O@*_'(`!@```$$``80":C(5`2U(5`
+M2YB@K),IK(<$\6^`K\<@`&````00`!A`)J,A4!+4A4!+F*"LDRFLAP3Q;X"O
+MQR``8```!!``&$`FHR%0$M2%0$N8H*R3*:R'!/%O@*_'(`!@```$$``80":C
+M(5`2U(5`2YB@K),IK(<$\6^`K\<@`&````00`!A`)J,A4!+4A4!+F*"LDRFL
+MAP3Q;X"OQR``8```!!``&$`FHR%0$M2%0$N8H*R3*:R'!/%O@*_'(`!@```$
+M$``80":C(5`2U(5`2YB@K),IK(<$\6^`K\<@`&````00`!A`)J,A4!+4A4!+
+MF*"LDRFLAP3Q;X"OQR``8```!!``&$`FHR%0$M2%0$N8H*R3*:R'!/%O@*_'
+M(`!@```$$``80":C(5`2U(5`2YB@K),IK(<$\6^`K\<@`&````00`!A`)J,A
+M4!+4A4!+F*"LDRFLAP3Q;X"OQR``8```!!``&$`FHR%0$M2%0$N8H*R3*:R'
+M!/%O@*_'(`!@```$$``80":C(5`2U(5`2YB@K),IK(<$\6^`K\<@`&````00
+M`!A`)J,A4!+4A4!+F*"LDRFLAP3Q;X"OQR``8```!!``&$`FHR%0$M2%0$N8
+MH*R3*:R'!/%O@*_'(`!@```$$``80":C(5`2U(5`2YB@K),IK(KI-J(`.S;_
+M[O_KZIKZ`[_T'_?OL''[3!(R1`(```"`(``A3^1@`6=-9:!3`DD(:)ZC%)Z9
+M33:@>DT]"`>IH&@`#1Z)M3"-#(T#$4:I[4U`/4T-````````````!PT-&31H
+MT::&1D,(`R`&0::``!D#(`D4IZIZ0-`T````````````]&II7`12+EV$9U/B
+MB!*_@T]?CB(F_;A17-%[AD,R8`JV@70VUC,Y6`FQ"$@1<L2$)(`2[M+DHCF`
+M0)`D)8!-P/',(82"IS(@R>@M3"$@0)"3F`Y&Q/<D<N339,\$GB`#`Q1SK=@R
+M<HD1R!2\A(3.H3EOJB05)8.6&%6&J\9CF>$XJBC2?^*)EA9:/'NE+<&E/_K^
+M-Z0($"0E11*I%KQK=*`$'<N+G-F@9[YHI1'6M:8%.$2785901!%"0`U7%A/<
+MPA@I4`8&N@"3L[Y!8V2Y*M8>NO&"YPP%*V4`5T>42;8B[:=M@:9#;@')!UFD
+M8$)CB(&5G68PD`TD`[D"*AV+N\79)G^Q/SOS[CLC2P3T;\WCMH#X(5\++`-#
+ML-+?DXEFS9H@P06,,HP2-X<1W%B*AY`JW`=,R?NG%O?)8;N6134$BXK2#F&&
+M*"8&QUDIX$R\2.;XAFVSP/=K'6.!M8;%=UGWD84CA7$<.IJ0]NS'LEV@J(Q[
+MA='$&9W%-><1MMG8-JCG-DG+D,4"H>!`EJIEKV61?:5TW2M97U?>B%$S@N0G
+M4!Q5$=A,ES!<J\):&C=5);/*T,$9.=>Z%;4P):4`FG@`X=YHB%#"PIU084Y4
+M<J@33)%J*]&*'15=HQ"PK3%#K"--<BB@FF*H%$2]4Y]`\FT7`^32RN'76OEC
+M>@/JFD\8E9G8Y\8:0TJYP'=17J(:!3R>)1/FE%'Z2XBYRI259/935.OC-50:
+M#[&,:!IG0R$A"P8>:89+I+'"&$B")A'5G8B+ZJ14JXH:'.:@IH0&T(0($A+"
+/#6J,VO\7<D4X4)!E$I5"
+`
+end
diff --git a/contrib/netbsd-tests/fs/cd9660/t_high_ino_big_file.sh b/contrib/netbsd-tests/fs/cd9660/t_high_ino_big_file.sh
new file mode 100755
index 000000000000..c38f10300bb9
--- /dev/null
+++ b/contrib/netbsd-tests/fs/cd9660/t_high_ino_big_file.sh
@@ -0,0 +1,118 @@
+# $NetBSD: t_high_ino_big_file.sh,v 1.4 2014/07/07 22:06:02 pgoyette Exp $
+#
+# Copyright (c) 2014 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+# The image used in these tests has been provided by Thomas Schmitt under
+# the following license (see PR kern/48787 for details how to recreate it):
+#
+# Copyright (c) 1999 - 2008, Thomas Schmitt (scdbackup@gmx.net)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# Neither the name of Thomas Schmitt nor the names of his contributors
+# may be used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+# CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+# DAMAGE.
+#
+# ------------------------------------------------------------------------
+# This is the BSD license as stated July 22 1999 with
+# <OWNER>="Thomas Schmitt (scdbackup@gmx.net)",
+# <ORGANIZATION>="Thomas Schmitt" and <YEAR>="1999"
+# an Open Source license approved by opensource.org
+#
+
+mntpnt=""
+
+atf_test_case pr_kern_48787 cleanup
+pr_kern_48787_head() {
+ atf_set "descr" "Verifies 32bit overflow isssues from PR kern/48787 are fixed"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "rump_cd9660 bunzip2 stat"
+ atf_set "timeout" 6000
+}
+
+pr_kern_48787_body() {
+ avail=$( df -Pk . | awk '{if (NR==2) print $4}' )
+ if [ $avail -lt 4500000 ]; then
+ atf_skip "not enough free disk space, have ${avail} Kbytes, need ~ 4500000 Kbytes"
+ fi
+ bunzip2 < $(atf_get_srcdir)/pr_48787.image.bz2 > pr_48787.image
+ mntpnt=$(pwd)/mnt
+ mkdir ${mntpnt}
+ rump_cd9660 -o norrip ./pr_48787.image ${mntpnt}
+ if [ ! -r ${mntpnt}/small_file ]; then
+ atf_fail "${mntpnt}/small_file does not exist"
+ fi
+ if [ ! -r ${mntpnt}/my/large_file ]; then
+ atf_fail "${mntpnt}/my/large_file does not exist"
+ fi
+ umount ${mntpnt}
+ rump_cd9660 ./pr_48787.image ${mntpnt}
+ if [ ! -r ${mntpnt}/small_file ]; then
+ atf_fail "${mntpnt}/small_file does not exist"
+ fi
+ if [ ! -r ${mntpnt}/my/large_file ]; then
+ atf_fail "${mntpnt}/my/large_file does not exist"
+ fi
+ echo "this assumes current cd9660 inode encoding - adapt on changes"
+ atf_check -o match:"^4329541966$" stat -f "%i" ${mntpnt}/small_file
+ atf_check -o match:"^4329545920$" stat -f "%i" ${mntpnt}/my/large_file
+ umount ${mntpnt}
+ touch "done"
+}
+
+pr_kern_48787_cleanup() {
+ if [ ! -f done ]; then
+ if [ "x${mntpnt}" != "x" ]; then
+ umount -f ${mntpnt} || true
+ fi
+ fi
+}
+
+atf_init_test_cases() {
+ atf_add_test_case pr_kern_48787
+}
diff --git a/contrib/netbsd-tests/fs/common/fstest_ext2fs.c b/contrib/netbsd-tests/fs/common/fstest_ext2fs.c
new file mode 100644
index 000000000000..85bb79f7ebd0
--- /dev/null
+++ b/contrib/netbsd-tests/fs/common/fstest_ext2fs.c
@@ -0,0 +1,139 @@
+/* $NetBSD: fstest_ext2fs.c,v 1.2 2010/07/30 16:15:05 pooka Exp $ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nicolas Joly.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <atf-c.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <ufs/ufs/ufsmount.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_fsmacros.h"
+
+struct ext2fstestargs {
+ struct ufs_args ta_uargs;
+ char ta_devpath[MAXPATHLEN];
+ char ta_imgpath[MAXPATHLEN];
+};
+
+int
+ext2fs_fstest_newfs(const atf_tc_t *tc, void **buf, const char *image,
+ off_t size, void *fspriv)
+{
+ char cmd[1024];
+ int res;
+ static unsigned int num = 0;
+ struct ext2fstestargs *args;
+
+ size /= 512;
+ snprintf(cmd, 1024, "newfs_ext2fs -F -s %"PRId64" %s >/dev/null",
+ size, image);
+ res = system(cmd);
+ if (res != 0)
+ return res;
+
+ res = rump_init();
+ if (res != 0)
+ return res;
+
+ args = calloc(1, sizeof(*args));
+ if (args == NULL)
+ return -1;
+
+ snprintf(args->ta_devpath, MAXPATHLEN, "/dev/device%d.ext2fs", num);
+ snprintf(args->ta_imgpath, MAXPATHLEN, "%s", image);
+ args->ta_uargs.fspec = args->ta_devpath;
+
+ res = rump_pub_etfs_register(args->ta_devpath, image, RUMP_ETFS_BLK);
+ if (res != 0) {
+ free(args);
+ return res;
+ }
+
+ *buf = args;
+ num++;
+
+ return res;
+}
+
+int
+ext2fs_fstest_delfs(const atf_tc_t *tc, void *buf)
+{
+ int res;
+ struct ext2fstestargs *args = buf;
+
+ res = rump_pub_etfs_remove(args->ta_devpath);
+ if (res != 0)
+ return res;
+
+ res = unlink(args->ta_imgpath);
+ if (res != 0)
+ return res;
+
+ free(args);
+
+ return 0;
+}
+
+int
+ext2fs_fstest_mount(const atf_tc_t *tc, void *buf, const char *path, int flags)
+{
+ int res;
+ struct ext2fstestargs *args = buf;
+
+ res = rump_sys_mkdir(path, 0777);
+ if (res == -1)
+ return res;
+
+ res = rump_sys_mount(MOUNT_EXT2FS, path, flags, &args->ta_uargs,
+ sizeof(args->ta_uargs));
+ return res;
+}
+
+int
+ext2fs_fstest_unmount(const atf_tc_t *tc, const char *path, int flags)
+{
+ int res;
+
+ res = rump_sys_unmount(path, flags);
+ if (res == -1)
+ return res;
+
+ res = rump_sys_rmdir(path);
+ return res;
+}
diff --git a/contrib/netbsd-tests/fs/common/fstest_ffs.c b/contrib/netbsd-tests/fs/common/fstest_ffs.c
new file mode 100644
index 000000000000..7ae4e8f5970c
--- /dev/null
+++ b/contrib/netbsd-tests/fs/common/fstest_ffs.c
@@ -0,0 +1,156 @@
+/* $NetBSD: fstest_ffs.c,v 1.6 2012/08/05 02:03:05 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nicolas Joly.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <atf-c.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <ufs/ufs/ufsmount.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_fsmacros.h"
+
+struct ffstestargs {
+ struct ufs_args ta_uargs;
+ char ta_devpath[MAXPATHLEN];
+ char ta_imgpath[MAXPATHLEN];
+};
+
+int
+ffs_fstest_newfs(const atf_tc_t *tc, void **buf, const char *image, off_t size,
+ void *fspriv)
+{
+ char cmd[1024];
+ int res;
+ static unsigned int num = 0;
+ struct ffstestargs *args;
+ struct sigaction act, oact;
+
+ size /= 512;
+ snprintf(cmd, 1024, "newfs -F -s %"PRId64" %s >/dev/null", size, image);
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = SIG_DFL;
+ sigaction(SIGCHLD, &act, &oact);
+ res = system(cmd);
+ sigaction(SIGCHLD, &oact, NULL);
+ if (res != 0)
+ return res;
+
+ res = rump_init();
+ if (res != 0)
+ return res;
+
+ args = calloc(1, sizeof(*args));
+ if (args == NULL)
+ return -1;
+
+ snprintf(args->ta_devpath, MAXPATHLEN, "/dev/device%d.ffs", num);
+ snprintf(args->ta_imgpath, MAXPATHLEN, "%s", image);
+ args->ta_uargs.fspec = args->ta_devpath;
+
+ res = rump_pub_etfs_register(args->ta_devpath, image, RUMP_ETFS_BLK);
+ if (res != 0) {
+ free(args);
+ return res;
+ }
+
+ *buf = args;
+ num++;
+
+ return 0;
+}
+__strong_alias(ffslog_fstest_newfs,ffs_fstest_newfs);
+
+int
+ffs_fstest_delfs(const atf_tc_t *tc, void *buf)
+{
+ int res;
+ struct ffstestargs *args = buf;
+
+ res = rump_pub_etfs_remove(args->ta_devpath);
+ if (res != 0) {
+ errno = res;
+ return -1;
+ }
+
+ res = unlink(args->ta_imgpath);
+ if (res != 0)
+ return res;
+
+ free(args);
+
+ return 0;
+}
+__strong_alias(ffslog_fstest_delfs,ffs_fstest_delfs);
+
+int
+ffs_fstest_mount(const atf_tc_t *tc, void *buf, const char *path, int flags)
+{
+ int res;
+ struct ffstestargs *args = buf;
+
+ res = rump_sys_mkdir(path, 0777);
+ if (res == -1)
+ return res;
+
+ res = rump_sys_mount(MOUNT_FFS, path, flags, &args->ta_uargs,
+ sizeof(args->ta_uargs));
+ return res;
+}
+
+int
+ffslog_fstest_mount(const atf_tc_t *tc, void *buf, const char *path, int flags)
+{
+
+ return ffs_fstest_mount(tc, buf, path, flags | MNT_LOG);
+}
+
+int
+ffs_fstest_unmount(const atf_tc_t *tc, const char *path, int flags)
+{
+ int res;
+
+ res = rump_sys_unmount(path, flags);
+ if (res == -1)
+ return res;
+
+ res = rump_sys_rmdir(path);
+ return res;
+}
+__strong_alias(ffslog_fstest_unmount,ffs_fstest_unmount);
diff --git a/contrib/netbsd-tests/fs/common/fstest_lfs.c b/contrib/netbsd-tests/fs/common/fstest_lfs.c
new file mode 100644
index 000000000000..597ca23bbaa8
--- /dev/null
+++ b/contrib/netbsd-tests/fs/common/fstest_lfs.c
@@ -0,0 +1,190 @@
+/* $NetBSD: fstest_lfs.c,v 1.5 2015/08/30 18:27:26 dholland Exp $ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nicolas Joly.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <atf-c.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <ufs/ufs/ufsmount.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_fsmacros.h"
+#include "mount_lfs.h"
+
+struct lfstestargs {
+ struct ufs_args ta_uargs;
+ pthread_t ta_cleanerthread;
+ sem_t ta_cleanerloop;
+ char ta_devpath[MAXPATHLEN];
+ char ta_imgpath[MAXPATHLEN];
+ char ta_mntpath[MAXPATHLEN];
+ char ta_hostpath[MAXPATHLEN];
+};
+
+int
+lfs_fstest_newfs(const atf_tc_t *tc, void **buf, const char *image, off_t size,
+ void *fspriv)
+{
+ char cmd[1024];
+ int res;
+ static unsigned int num = 0;
+ struct lfstestargs *args;
+
+ size /= 512;
+ snprintf(cmd, 1024, "newfs_lfs -D -F -s %"PRId64" ./%s >/dev/null",
+ size, image);
+ res = system(cmd);
+ if (res != 0)
+ return res;
+
+ res = rump_init();
+ if (res != 0)
+ return res;
+
+ args = calloc(1, sizeof(*args));
+ if (args == NULL)
+ return -1;
+
+ strcpy(args->ta_hostpath, image);
+ snprintf(args->ta_devpath, MAXPATHLEN, "/dev/device%d.lfs", num);
+ snprintf(args->ta_imgpath, MAXPATHLEN, "%s", image);
+ args->ta_uargs.fspec = args->ta_devpath;
+ sem_init(&args->ta_cleanerloop, 0, 0);
+
+ res = rump_pub_etfs_register(args->ta_devpath, image, RUMP_ETFS_BLK);
+ if (res != 0) {
+ free(args);
+ return res;
+ }
+
+ *buf = args;
+ num++;
+
+ return 0;
+}
+
+int
+lfs_fstest_delfs(const atf_tc_t *tc, void *buf)
+{
+ int res;
+ struct lfstestargs *args = buf;
+
+ res = rump_pub_etfs_remove(args->ta_devpath);
+ if (res != 0)
+ return res;
+
+ res = unlink(args->ta_imgpath);
+ if (res != 0)
+ return res;
+
+ pthread_join(args->ta_cleanerthread, NULL);
+ free(args);
+
+ return 0;
+}
+
+static void *
+cleaner(void *arg)
+{
+ char thepath[MAXPATHLEN];
+ struct lfstestargs *args = arg;
+ const char *the_argv[7];
+ char buf[64];
+
+ /* this inspired by the cleaner code. fixme */
+ sprintf(thepath, "/dev/r%s", args->ta_devpath+5);
+ rump_pub_etfs_register(thepath, args->ta_hostpath, RUMP_ETFS_CHR);
+ sprintf(buf, "%p", &args->ta_cleanerloop);
+
+ the_argv[0] = "megamaid";
+ the_argv[1] = "-D"; /* don't fork() & detach */
+ the_argv[2] = "-S";
+ the_argv[3] = buf;
+ the_argv[4] = args->ta_mntpath;
+ the_argv[5] = NULL;
+
+ /* xxxatf */
+ optind = 1;
+ opterr = 1;
+
+ lfs_cleaner_main(5, __UNCONST(the_argv));
+
+ return NULL;
+}
+
+int
+lfs_fstest_mount(const atf_tc_t *tc, void *buf, const char *path, int flags)
+{
+ struct lfstestargs *args = buf;
+ int res;
+
+ res = rump_sys_mkdir(path, 0777);
+ if (res == -1)
+ return res;
+
+ res = rump_sys_mount(MOUNT_LFS, path, flags, &args->ta_uargs,
+ sizeof(args->ta_uargs));
+ if (res == -1)
+ return res;
+
+ strcpy(args->ta_mntpath, path);
+ res = pthread_create(&args->ta_cleanerthread, NULL, cleaner, args);
+ if (res)
+ return res;
+
+ /* wait for cleaner to initialize */
+ sem_wait(&args->ta_cleanerloop);
+
+ return 0;
+}
+
+int
+lfs_fstest_unmount(const atf_tc_t *tc, const char *path, int flags)
+{
+ int res;
+
+ res = rump_sys_unmount(path, flags);
+ if (res == -1) {
+ return res;
+ }
+
+ res = rump_sys_rmdir(path);
+ return res;
+}
diff --git a/contrib/netbsd-tests/fs/common/fstest_msdosfs.c b/contrib/netbsd-tests/fs/common/fstest_msdosfs.c
new file mode 100644
index 000000000000..2c94e3f92fa8
--- /dev/null
+++ b/contrib/netbsd-tests/fs/common/fstest_msdosfs.c
@@ -0,0 +1,140 @@
+/* $NetBSD: fstest_msdosfs.c,v 1.3 2012/03/26 15:10:26 njoly Exp $ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nicolas Joly.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <atf-c.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <msdosfs/msdosfsmount.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_fsmacros.h"
+
+struct msdosfstestargs {
+ struct msdosfs_args ta_uargs;
+ char ta_devpath[MAXPATHLEN];
+ char ta_imgpath[MAXPATHLEN];
+};
+
+int
+msdosfs_fstest_newfs(const atf_tc_t *tc, void **buf, const char *image,
+ off_t size, void *fspriv)
+{
+ char cmd[1024];
+ int res;
+ static unsigned int num = 0;
+ struct msdosfstestargs *args;
+
+ size /= 512; size -= (size % 63);
+ snprintf(cmd, 1024, "newfs_msdos -C %"PRId64"s %s >/dev/null",
+ size, image);
+ res = system(cmd);
+ if (res != 0)
+ return res;
+
+ res = rump_init();
+ if (res != 0)
+ return res;
+
+ args = calloc(1, sizeof(*args));
+ if (args == NULL)
+ return -1;
+
+ snprintf(args->ta_devpath, MAXPATHLEN, "/dev/device%d.msdosfs", num);
+ snprintf(args->ta_imgpath, MAXPATHLEN, "%s", image);
+ args->ta_uargs.fspec = args->ta_devpath;
+ args->ta_uargs.mask = 0755;
+
+ res = rump_pub_etfs_register(args->ta_devpath, image, RUMP_ETFS_BLK);
+ if (res != 0) {
+ free(args);
+ return res;
+ }
+
+ *buf = args;
+ num++;
+
+ return 0;
+}
+
+int
+msdosfs_fstest_delfs(const atf_tc_t *tc, void *buf)
+{
+ int res;
+ struct msdosfstestargs *args = buf;
+
+ res = rump_pub_etfs_remove(args->ta_devpath);
+ if (res != 0)
+ return res;
+
+ res = unlink(args->ta_imgpath);
+ if (res != 0)
+ return res;
+
+ free(args);
+
+ return 0;
+}
+
+int
+msdosfs_fstest_mount(const atf_tc_t *tc, void *buf, const char *path, int flags)
+{
+ int res;
+ struct msdosfstestargs *args = buf;
+
+ res = rump_sys_mkdir(path, 0777);
+ if (res == -1)
+ return res;
+
+ res = rump_sys_mount(MOUNT_MSDOS, path, flags, &args->ta_uargs,
+ sizeof(args->ta_uargs));
+ return res;
+}
+
+int
+msdosfs_fstest_unmount(const atf_tc_t *tc, const char *path, int flags)
+{
+ int res;
+
+ res = rump_sys_unmount(path, flags);
+ if (res == -1)
+ return res;
+
+ res = rump_sys_rmdir(path);
+ return res;
+}
diff --git a/contrib/netbsd-tests/fs/common/fstest_nfs.c b/contrib/netbsd-tests/fs/common/fstest_nfs.c
new file mode 100644
index 000000000000..5ef256f1be41
--- /dev/null
+++ b/contrib/netbsd-tests/fs/common/fstest_nfs.c
@@ -0,0 +1,326 @@
+/* $NetBSD: fstest_nfs.c,v 1.9 2011/02/28 21:08:46 pooka Exp $ */
+
+/*
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/statvfs.h>
+#include <sys/wait.h>
+
+#include <assert.h>
+#include <atf-c.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <pthread.h>
+#include <puffs.h>
+#include <puffsdump.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_fsmacros.h"
+#include "mount_nfs.h"
+#include "../../net/config/netconfig.c"
+
+#define SERVERADDR "10.3.2.1"
+#define SERVERROADDR "10.4.2.1"
+#define CLIENTADDR "10.3.2.2"
+#define CLIENTROADDR "10.4.2.2"
+#define NETNETMASK "255.255.255.0"
+#define EXPORTPATH "/myexport"
+
+static void
+childfail(int status)
+{
+
+ atf_tc_fail("child died");
+}
+
+/* fork rump nfsd, configure interface */
+static int
+donewfs(const atf_tc_t *tc, void **argp,
+ const char *image, off_t size, void *fspriv)
+{
+ const char *srcdir;
+ char *nfsdargv[16];
+ char nfsdpath[MAXPATHLEN];
+ char imagepath[MAXPATHLEN];
+ char ethername[MAXPATHLEN], ethername_ro[MAXPATHLEN];
+ char ifname[IFNAMSIZ], ifname_ro[IFNAMSIZ];
+ char cwd[MAXPATHLEN];
+ struct nfstestargs *args;
+ pid_t childpid;
+ int pipes[2];
+ int devnull;
+
+ /*
+ * First, we start the nfs service.
+ */
+ srcdir = atf_tc_get_config_var(tc, "srcdir");
+ sprintf(nfsdpath, "%s/../nfs/nfsservice/rumpnfsd", srcdir);
+ sprintf(ethername, "/%s/%s.etherbus", getcwd(cwd, sizeof(cwd)), image);
+ sprintf(ethername_ro, "%s_ro", ethername);
+ sprintf(imagepath, "/%s/%s", cwd, image);
+
+ nfsdargv[0] = nfsdpath;
+ nfsdargv[1] = ethername;
+ nfsdargv[2] = ethername_ro;
+ nfsdargv[3] = __UNCONST(SERVERADDR);
+ nfsdargv[4] = __UNCONST(SERVERROADDR);
+ nfsdargv[5] = __UNCONST(NETNETMASK);
+ nfsdargv[6] = __UNCONST(EXPORTPATH);
+ nfsdargv[7] = imagepath;
+ nfsdargv[8] = NULL;
+
+ signal(SIGCHLD, childfail);
+ if (pipe(pipes) == -1)
+ return errno;
+
+ switch ((childpid = fork())) {
+ case 0:
+ if (chdir(dirname(nfsdpath)) == -1)
+ err(1, "chdir");
+ close(pipes[0]);
+ if (dup2(pipes[1], 3) == -1)
+ err(1, "dup2");
+ if (execvp(nfsdargv[0], nfsdargv) == -1)
+ err(1, "execvp");
+ case -1:
+ return errno;
+ default:
+ close(pipes[1]);
+ break;
+ }
+
+ /*
+ * Ok, nfsd has been run. The following sleep helps with the
+ * theoretical problem that nfsd can't start fast enough to
+ * process our mount request and we end up doing a timeout
+ * before the mount. This would take several seconds. So
+ * try to make sure nfsd is up&running already at this stage.
+ */
+ if (read(pipes[0], &devnull, 4) == -1)
+ return errno;
+
+ /*
+ * Configure our networking interface.
+ */
+ rump_init();
+ netcfg_rump_makeshmif(ethername, ifname);
+ netcfg_rump_if(ifname, CLIENTADDR, NETNETMASK);
+ netcfg_rump_makeshmif(ethername_ro, ifname_ro);
+ netcfg_rump_if(ifname_ro, CLIENTROADDR, NETNETMASK);
+
+ /*
+ * That's it. The rest is done in mount, since we don't have
+ * the mountpath available here.
+ */
+ args = malloc(sizeof(*args));
+ if (args == NULL)
+ return errno;
+ memset(args, 0, sizeof(*args));
+ args->ta_childpid = childpid;
+ strcpy(args->ta_ethername, ethername);
+
+ *argp = args;
+
+ return 0;
+}
+
+int
+nfs_fstest_newfs(const atf_tc_t *tc, void **argp,
+ const char *image, off_t size, void *fspriv)
+{
+
+ return donewfs(tc, argp, image, size, fspriv);
+}
+
+int
+nfsro_fstest_newfs(const atf_tc_t *tc, void **argp,
+ const char *image, off_t size, void *fspriv)
+{
+
+ return donewfs(tc, argp, image, size, fspriv);
+}
+
+/* mount the file system */
+static int
+domount(const atf_tc_t *tc, void *arg, const char *serverpath,
+ const char *path, int flags)
+{
+ char canon_dev[MAXPATHLEN], canon_dir[MAXPATHLEN];
+ const char *nfscliargs[] = {
+ "nfsclient",
+ serverpath,
+ path,
+ NULL,
+ };
+ struct nfs_args args;
+ int mntflags;
+
+ if (rump_sys_mkdir(path, 0777) == -1)
+ return errno;
+
+ /* XXX: atf does not reset values */
+ optind = 1;
+ opterr = 1;
+
+ /*
+ * We use nfs parseargs here, since as a side effect it
+ * takes care of the RPC hulabaloo.
+ */
+ mount_nfs_parseargs(__arraycount(nfscliargs)-1, __UNCONST(nfscliargs),
+ &args, &mntflags, canon_dev, canon_dir);
+
+ if (rump_sys_mount(MOUNT_NFS, path, flags, &args, sizeof(args)) == -1) {
+ return errno;
+ }
+
+ return 0;
+}
+
+int
+nfs_fstest_mount(const atf_tc_t *tc, void *arg, const char *path, int flags)
+{
+
+ return domount(tc, arg, SERVERADDR ":" EXPORTPATH, path, flags);
+}
+
+/*
+ * This is where the magic happens!
+ *
+ * If we are mounting r/w, do the normal thing. However, if we are
+ * doing a r/o mount, switch use the r/o server export address
+ * and do a r/w mount. This way we end up testing the r/o export policy
+ * of the server! (yes, slightly questionable semantics, but at least
+ * we notice very quickly if our assumption is broken in the future ;)
+ */
+int
+nfsro_fstest_mount(const atf_tc_t *tc, void *arg, const char *path, int flags)
+{
+
+ if (flags & MNT_RDONLY) {
+ flags &= ~MNT_RDONLY;
+ return domount(tc, arg, SERVERROADDR":"EXPORTPATH, path, flags);
+ } else {
+ return domount(tc, arg, SERVERADDR":"EXPORTPATH, path, flags);
+ }
+}
+
+static int
+dodelfs(const atf_tc_t *tc, void *arg)
+{
+
+ /*
+ * XXX: no access to "args" since we're called from "cleanup".
+ * Trust atf to kill nfsd process and remove etherfile.
+ */
+#if 0
+ /*
+ * It's highly expected that the child will die next, so we
+ * don't need that information anymore thank you very many.
+ */
+ signal(SIGCHLD, SIG_IGN);
+
+ /*
+ * Just KILL it. Sending it SIGTERM first causes it to try
+ * to send some unmount RPCs, leading to sticky situations.
+ */
+ kill(args->ta_childpid, SIGKILL);
+ wait(&status);
+
+ /* remove ethernet bus */
+ if (unlink(args->ta_ethername) == -1)
+ atf_tc_fail_errno("unlink ethername");
+#endif
+
+ return 0;
+}
+
+int
+nfs_fstest_delfs(const atf_tc_t *tc, void *arg)
+{
+
+ return dodelfs(tc, arg);
+}
+
+int
+nfsro_fstest_delfs(const atf_tc_t *tc, void *arg)
+{
+
+ return dodelfs(tc, arg);
+}
+
+static int
+dounmount(const atf_tc_t *tc, const char *path, int flags)
+{
+ int status, i, sverrno;
+
+ /*
+ * NFS handles sillyrenames in an workqueue. Some of them might
+ * be still in the queue even if all user activity has ceased.
+ * We try to unmount for 2 seconds to give them a chance
+ * to flush out.
+ *
+ * PR kern/43799
+ */
+ for (i = 0; i < 20; i++) {
+ if ((status = rump_sys_unmount(path, flags)) == 0)
+ break;
+ sverrno = errno;
+ if (sverrno != EBUSY)
+ break;
+ usleep(100000);
+ }
+ if (status == -1)
+ return sverrno;
+
+ if (rump_sys_rmdir(path) == -1)
+ return errno;
+
+ return 0;
+}
+
+int
+nfs_fstest_unmount(const atf_tc_t *tc, const char *path, int flags)
+{
+
+ return dounmount(tc, path, flags);
+}
+
+int
+nfsro_fstest_unmount(const atf_tc_t *tc, const char *path, int flags)
+{
+
+ return dounmount(tc, path, flags);
+}
diff --git a/contrib/netbsd-tests/fs/common/fstest_puffs.c b/contrib/netbsd-tests/fs/common/fstest_puffs.c
new file mode 100644
index 000000000000..14e4bfba43ba
--- /dev/null
+++ b/contrib/netbsd-tests/fs/common/fstest_puffs.c
@@ -0,0 +1,452 @@
+/* $NetBSD: fstest_puffs.c,v 1.11 2013/09/09 19:47:38 pooka Exp $ */
+
+/*
+ * Copyright (c) 2010, 2011 The NetBSD Foundation, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/statvfs.h>
+#include <sys/wait.h>
+
+#include <assert.h>
+#include <atf-c.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <puffs.h>
+#include <puffsdump.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_fsmacros.h"
+
+#define BUFSIZE (128*1024)
+#define DTFS_DUMP "-o","dump"
+
+static bool mayquit = false;
+
+static ssize_t
+xread(int fd, void *vp, size_t n)
+{
+ size_t left;
+
+ left = n;
+ do {
+ ssize_t ssz;
+
+ ssz = read(fd, vp, left);
+ if (ssz == -1) {
+ return ssz;
+ }
+ left -= ssz;
+ vp = (char *)vp + ssz;
+ } while (left > 0);
+ return n;
+}
+
+static ssize_t
+xwrite(int fd, const void *vp, size_t n)
+{
+ size_t left;
+
+ left = n;
+ do {
+ ssize_t ssz;
+
+ ssz = write(fd, vp, left);
+ if (ssz == -1) {
+ return ssz;
+ }
+ left -= ssz;
+ vp = (const char *)vp + ssz;
+ } while (left > 0);
+ return n;
+}
+
+/*
+ * Threads which shovel data between comfd and /dev/puffs.
+ * (cannot use polling since fd's are in different namespaces)
+ */
+static void *
+readshovel(void *arg)
+{
+ struct putter_hdr *phdr;
+ struct puffs_req *preq;
+ struct puffstestargs *args = arg;
+ char buf[BUFSIZE];
+ ssize_t n;
+ int comfd, puffsfd;
+
+ comfd = args->pta_servfd;
+ puffsfd = args->pta_rumpfd;
+
+ phdr = (void *)buf;
+ preq = (void *)buf;
+
+ rump_pub_lwproc_newlwp(1);
+
+ for (;;) {
+ n = rump_sys_read(puffsfd, buf, sizeof(*phdr));
+ if (n <= 0) {
+ fprintf(stderr, "readshovel r1 %zd / %d\n", n, errno);
+ break;
+ }
+
+ assert(phdr->pth_framelen < BUFSIZE);
+ n = rump_sys_read(puffsfd, buf+sizeof(*phdr),
+ phdr->pth_framelen - sizeof(*phdr));
+ if (n <= 0) {
+ fprintf(stderr, "readshovel r2 %zd / %d\n", n, errno);
+ break;
+ }
+
+ /* Analyze request */
+ if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VFS) {
+ assert(preq->preq_optype < PUFFS_VFS_MAX);
+ args->pta_vfs_toserv_ops[preq->preq_optype]++;
+ } else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN) {
+ assert(preq->preq_optype < PUFFS_VN_MAX);
+ args->pta_vn_toserv_ops[preq->preq_optype]++;
+ }
+
+ n = phdr->pth_framelen;
+ if (xwrite(comfd, buf, n) != n) {
+ fprintf(stderr, "readshovel write %zd / %d\n", n, errno);
+ break;
+ }
+ }
+
+ if (n != 0 && mayquit == false)
+ abort();
+ return NULL;
+}
+
+static void *
+writeshovel(void *arg)
+{
+ struct puffstestargs *args = arg;
+ struct putter_hdr *phdr;
+ struct puffs_req *preq;
+ char buf[BUFSIZE];
+ size_t toread;
+ ssize_t n;
+ int comfd, puffsfd;
+
+ rump_pub_lwproc_newlwp(1);
+
+ comfd = args->pta_servfd;
+ puffsfd = args->pta_rumpfd;
+
+ phdr = (struct putter_hdr *)buf;
+ preq = (void *)buf;
+
+ for (;;) {
+ uint64_t off;
+
+ /*
+ * Need to write everything to the "kernel" in one chunk,
+ * so make sure we have it here.
+ */
+ off = 0;
+ toread = sizeof(struct putter_hdr);
+ assert(toread < BUFSIZE);
+ do {
+ n = xread(comfd, buf+off, toread);
+ if (n <= 0) {
+ fprintf(stderr, "writeshovel read %zd / %d\n",
+ n, errno);
+ goto out;
+ }
+ off += n;
+ if (off >= sizeof(struct putter_hdr))
+ toread = phdr->pth_framelen - off;
+ else
+ toread = off - sizeof(struct putter_hdr);
+ } while (toread);
+
+ if (__predict_false(
+ PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VFS
+ && preq->preq_optype == PUFFS_VFS_UNMOUNT)) {
+ if (preq->preq_rv == 0)
+ mayquit = true;
+ }
+
+ n = rump_sys_write(puffsfd, buf, phdr->pth_framelen);
+ if ((size_t)n != phdr->pth_framelen) {
+ fprintf(stderr, "writeshovel wr %zd / %d\n", n, errno);
+ break;
+ }
+ }
+
+ out:
+ if (n != 0)
+ abort();
+ return NULL;
+}
+
+static void
+rumpshovels(struct puffstestargs *args)
+{
+ pthread_t pt;
+ int rv;
+
+ if ((rv = rump_init()) == -1)
+ err(1, "rump_init");
+
+ if (pthread_create(&pt, NULL, readshovel, args) == -1)
+ err(1, "read shovel");
+ pthread_detach(pt);
+
+ if (pthread_create(&pt, NULL, writeshovel, args) == -1)
+ err(1, "write shovel");
+ pthread_detach(pt);
+}
+
+static void
+childfail(int sign)
+{
+
+ atf_tc_fail("child died"); /* almost signal-safe */
+}
+
+struct puffstestargs *theargs; /* XXX */
+
+/* XXX: we don't support size */
+static int
+donewfs(const atf_tc_t *tc, void **argp,
+ const char *image, off_t size, void *fspriv, char **theargv)
+{
+ struct puffstestargs *args;
+ pid_t childpid;
+ int *pflags;
+ char comfd[16];
+ int sv[2];
+ int mntflags;
+ size_t len;
+ ssize_t n;
+
+ *argp = NULL;
+
+ args = malloc(sizeof(*args));
+ if (args == NULL)
+ return errno;
+ memset(args, 0, sizeof(*args));
+
+ pflags = &args->pta_pflags;
+
+ /* Create sucketpair for communication with the real file server */
+ if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sv) == -1)
+ return errno;
+
+ signal(SIGCHLD, childfail);
+
+ switch ((childpid = fork())) {
+ case 0:
+ close(sv[1]);
+ snprintf(comfd, sizeof(sv[0]), "%d", sv[0]);
+ if (setenv("PUFFS_COMFD", comfd, 1) == -1)
+ return errno;
+
+ if (execvp(theargv[0], theargv) == -1)
+ return errno;
+ case -1:
+ return errno;
+ default:
+ close(sv[0]);
+ break;
+ }
+
+ /* read args */
+ if ((n = xread(sv[1], &len, sizeof(len))) != sizeof(len))
+ err(1, "mp 1 %zd", n);
+ if (len > MAXPATHLEN)
+ err(1, "mntpath > MAXPATHLEN");
+ if ((size_t)xread(sv[1], args->pta_dir, len) != len)
+ err(1, "mp 2");
+ if (xread(sv[1], &len, sizeof(len)) != sizeof(len))
+ err(1, "fn 1");
+ if (len > MAXPATHLEN)
+ err(1, "devpath > MAXPATHLEN");
+ if ((size_t)xread(sv[1], args->pta_dev, len) != len)
+ err(1, "fn 2");
+ if (xread(sv[1], &mntflags, sizeof(mntflags)) != sizeof(mntflags))
+ err(1, "mntflags");
+ if (xread(sv[1], &args->pta_pargslen, sizeof(args->pta_pargslen))
+ != sizeof(args->pta_pargslen))
+ err(1, "puffstest_args len");
+ args->pta_pargs = malloc(args->pta_pargslen);
+ if (args->pta_pargs == NULL)
+ err(1, "malloc");
+ if (xread(sv[1], args->pta_pargs, args->pta_pargslen)
+ != (ssize_t)args->pta_pargslen)
+ err(1, "puffstest_args");
+ if (xread(sv[1], pflags, sizeof(*pflags)) != sizeof(*pflags))
+ err(1, "pflags");
+
+ args->pta_childpid = childpid;
+ args->pta_servfd = sv[1];
+ strlcpy(args->pta_dev, image, sizeof(args->pta_dev));
+
+ *argp = theargs = args;
+
+ return 0;
+}
+
+int
+puffs_fstest_newfs(const atf_tc_t *tc, void **argp,
+ const char *image, off_t size, void *fspriv)
+{
+ char dtfs_path[MAXPATHLEN];
+ char *dtfsargv[6];
+ char **theargv;
+
+ /* build dtfs exec path from atf test dir */
+ sprintf(dtfs_path, "%s/../puffs/h_dtfs/h_dtfs",
+ atf_tc_get_config_var(tc, "srcdir"));
+
+ if (fspriv) {
+ theargv = fspriv;
+ theargv[0] = dtfs_path;
+ } else {
+ dtfsargv[0] = dtfs_path;
+ dtfsargv[1] = __UNCONST("-i");
+ dtfsargv[2] = __UNCONST("-s");
+ dtfsargv[3] = __UNCONST("dtfs");
+ dtfsargv[4] = __UNCONST("fictional");
+ dtfsargv[5] = NULL;
+
+ theargv = dtfsargv;
+ }
+
+ return donewfs(tc, argp, image, size, fspriv, theargv);
+}
+
+int
+p2k_ffs_fstest_newfs(const atf_tc_t *tc, void **argp,
+ const char *image, off_t size, void *fspriv)
+{
+ char *rumpffs_argv[5];
+ int rv;
+
+ rump_init();
+ if ((rv = ffs_fstest_newfs(tc, argp, image, size, fspriv)) != 0)
+ return rv;
+ if (mkdir("p2kffsfake", 0777) == -1 && errno != EEXIST)
+ return errno;
+
+ setenv("P2K_NODETACH", "1", 1);
+ rumpffs_argv[0] = __UNCONST("rump_ffs");
+ rumpffs_argv[1] = __UNCONST(image);
+ rumpffs_argv[2] = __UNCONST("p2kffsfake"); /* NOTUSED */
+ rumpffs_argv[3] = NULL;
+
+ if ((rv = donewfs(tc, argp, image, size, fspriv, rumpffs_argv)) != 0)
+ ffs_fstest_delfs(tc, argp);
+ return rv;
+}
+
+int
+puffs_fstest_mount(const atf_tc_t *tc, void *arg, const char *path, int flags)
+{
+ struct puffstestargs *pargs = arg;
+ int fd;
+
+ rump_init();
+ fd = rump_sys_open("/dev/puffs", O_RDWR);
+ if (fd == -1)
+ return fd;
+
+ if (rump_sys_mkdir(path, 0777) == -1)
+ return -1;
+
+ if (rump_sys_mount(MOUNT_PUFFS, path, flags,
+ pargs->pta_pargs, pargs->pta_pargslen) == -1) {
+ /* apply "to kill a child" to avoid atf hang (kludge) */
+ kill(pargs->pta_childpid, SIGKILL);
+ return -1;
+ }
+
+ pargs->pta_rumpfd = fd;
+ rumpshovels(pargs);
+
+ return 0;
+}
+__strong_alias(p2k_ffs_fstest_mount,puffs_fstest_mount);
+
+int
+puffs_fstest_delfs(const atf_tc_t *tc, void *arg)
+{
+
+ /* useless ... */
+ return 0;
+}
+
+int
+p2k_ffs_fstest_delfs(const atf_tc_t *tc, void *arg)
+{
+
+ return ffs_fstest_delfs(tc, arg);
+}
+
+int
+puffs_fstest_unmount(const atf_tc_t *tc, const char *path, int flags)
+{
+ struct puffstestargs *pargs = theargs;
+ int status;
+ int rv;
+
+ /* ok, child might exit here */
+ signal(SIGCHLD, SIG_IGN);
+
+ rv = rump_sys_unmount(path, flags);
+ if (rv)
+ return rv;
+
+ if ((rv = rump_sys_rmdir(path)) != 0)
+ return rv;
+
+ if (waitpid(pargs->pta_childpid, &status, WNOHANG) > 0)
+ return 0;
+ kill(pargs->pta_childpid, SIGTERM);
+ usleep(10);
+ if (waitpid(pargs->pta_childpid, &status, WNOHANG) > 0)
+ return 0;
+ kill(pargs->pta_childpid, SIGKILL);
+ usleep(500);
+ wait(&status);
+
+ rmdir("p2kffsfake");
+
+ return 0;
+}
+__strong_alias(p2k_ffs_fstest_unmount,puffs_fstest_unmount);
diff --git a/contrib/netbsd-tests/fs/common/fstest_rumpfs.c b/contrib/netbsd-tests/fs/common/fstest_rumpfs.c
new file mode 100644
index 000000000000..e4003db3d026
--- /dev/null
+++ b/contrib/netbsd-tests/fs/common/fstest_rumpfs.c
@@ -0,0 +1,90 @@
+/* $NetBSD: fstest_rumpfs.c,v 1.2 2014/03/16 10:28:03 njoly Exp $ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nicolas Joly.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <atf-c.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_fsmacros.h"
+
+int
+rumpfs_fstest_newfs(const atf_tc_t *tc, void **buf, const char *image,
+ off_t size, void *fspriv)
+{
+ char tmp[64];
+ int res;
+
+ snprintf(tmp, sizeof(tmp), "%"PRId64, size);
+ res = setenv("RUMP_MEMLIMIT", tmp, 0);
+ if (res == -1)
+ return res;
+
+ return rump_init();
+}
+
+int
+rumpfs_fstest_delfs(const atf_tc_t *tc, void *buf)
+{
+
+ return 0;
+}
+
+int
+rumpfs_fstest_mount(const atf_tc_t *tc, void *buf, const char *path, int flags)
+{
+ int res;
+
+ res = rump_sys_mkdir(path, 0777);
+ if (res == -1)
+ return res;
+
+ return rump_sys_mount(MOUNT_RUMPFS, path, flags, NULL, 0);
+}
+
+int
+rumpfs_fstest_unmount(const atf_tc_t *tc, const char *path, int flags)
+{
+ int res;
+
+ res = rump_sys_unmount(path, flags);
+ if (res == -1)
+ return res;
+
+ return rump_sys_rmdir(path);
+}
diff --git a/contrib/netbsd-tests/fs/common/fstest_sysvbfs.c b/contrib/netbsd-tests/fs/common/fstest_sysvbfs.c
new file mode 100644
index 000000000000..a7cf7f4cd8e1
--- /dev/null
+++ b/contrib/netbsd-tests/fs/common/fstest_sysvbfs.c
@@ -0,0 +1,139 @@
+/* $NetBSD: fstest_sysvbfs.c,v 1.2 2010/07/30 16:15:05 pooka Exp $ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nicolas Joly.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <atf-c.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <ufs/ufs/ufsmount.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_fsmacros.h"
+
+struct sysvbfstestargs {
+ struct ufs_args ta_uargs;
+ char ta_devpath[MAXPATHLEN];
+ char ta_imgpath[MAXPATHLEN];
+};
+
+int
+sysvbfs_fstest_newfs(const atf_tc_t *tc, void **buf, const char *image,
+ off_t size, void *fspriv)
+{
+ char cmd[1024];
+ int res;
+ static unsigned int num = 0;
+ struct sysvbfstestargs *args;
+
+ size /= 512;
+ snprintf(cmd, 1024, "newfs_sysvbfs -F -s %"PRId64" %s >/dev/null",
+ size, image);
+ res = system(cmd);
+ if (res != 0)
+ return res;
+
+ res = rump_init();
+ if (res != 0)
+ return res;
+
+ args = calloc(1, sizeof(*args));
+ if (args == NULL)
+ return -1;
+
+ snprintf(args->ta_devpath, MAXPATHLEN, "/dev/device%d.sysvbfs", num);
+ snprintf(args->ta_imgpath, MAXPATHLEN, "%s", image);
+ args->ta_uargs.fspec = args->ta_devpath;
+
+ res = rump_pub_etfs_register(args->ta_devpath, image, RUMP_ETFS_BLK);
+ if (res != 0) {
+ free(args);
+ return res;
+ }
+
+ *buf = args;
+ num++;
+
+ return 0;
+}
+
+int
+sysvbfs_fstest_delfs(const atf_tc_t *tc, void *buf)
+{
+ int res;
+ struct sysvbfstestargs *args = buf;
+
+ res = rump_pub_etfs_remove(args->ta_devpath);
+ if (res != 0)
+ return res;
+
+ res = unlink(args->ta_imgpath);
+ if (res != 0)
+ return res;
+
+ free(args);
+
+ return 0;
+}
+
+int
+sysvbfs_fstest_mount(const atf_tc_t *tc, void *buf, const char *path, int flags)
+{
+ int res;
+ struct sysvbfstestargs *args = buf;
+
+ res = rump_sys_mkdir(path, 0777);
+ if (res == -1)
+ return res;
+
+ res = rump_sys_mount(MOUNT_SYSVBFS, path, flags, &args->ta_uargs,
+ sizeof(args->ta_uargs));
+ return res;
+}
+
+int
+sysvbfs_fstest_unmount(const atf_tc_t *tc, const char *path, int flags)
+{
+ int res;
+
+ res = rump_sys_unmount(path, flags);
+ if (res == -1)
+ return res;
+
+ res = rump_sys_rmdir(path);
+ return res;
+}
diff --git a/contrib/netbsd-tests/fs/common/fstest_tmpfs.c b/contrib/netbsd-tests/fs/common/fstest_tmpfs.c
new file mode 100644
index 000000000000..83848432e21f
--- /dev/null
+++ b/contrib/netbsd-tests/fs/common/fstest_tmpfs.c
@@ -0,0 +1,112 @@
+/* $NetBSD: fstest_tmpfs.c,v 1.2 2010/07/30 16:15:05 pooka Exp $ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nicolas Joly.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <atf-c.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <fs/tmpfs/tmpfs_args.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_fsmacros.h"
+
+struct tmpfstestargs {
+ struct tmpfs_args ta_uargs;
+};
+
+int
+tmpfs_fstest_newfs(const atf_tc_t *tc, void **buf, const char *image,
+ off_t size, void *fspriv)
+{
+ int res;
+ struct tmpfstestargs *args;
+
+ res = rump_init();
+ if (res != 0)
+ return res;
+
+ args = calloc(1, sizeof(*args));
+ if (args == NULL)
+ return -1;
+
+ args->ta_uargs.ta_version = TMPFS_ARGS_VERSION;
+ args->ta_uargs.ta_root_mode = 0777;
+ args->ta_uargs.ta_size_max = size;
+
+ *buf = args;
+
+ return 0;
+}
+
+int
+tmpfs_fstest_delfs(const atf_tc_t *tc, void *buf)
+{
+ struct tmpfstestargs *args = buf;
+
+ free(args);
+
+ return 0;
+}
+
+int
+tmpfs_fstest_mount(const atf_tc_t *tc, void *buf, const char *path, int flags)
+{
+ int res;
+ struct tmpfstestargs *args = buf;
+
+ res = rump_sys_mkdir(path, 0777);
+ if (res == -1)
+ return res;
+
+ res = rump_sys_mount(MOUNT_TMPFS, path, flags, &args->ta_uargs,
+ sizeof(args->ta_uargs));
+ return res;
+}
+
+int
+tmpfs_fstest_unmount(const atf_tc_t *tc, const char *path, int flags)
+{
+ int res;
+
+ res = rump_sys_unmount(path, flags);
+ if (res == -1)
+ return res;
+
+ res = rump_sys_rmdir(path);
+ return res;
+}
diff --git a/contrib/netbsd-tests/fs/common/fstest_udf.c b/contrib/netbsd-tests/fs/common/fstest_udf.c
new file mode 100644
index 000000000000..3c9e017f388f
--- /dev/null
+++ b/contrib/netbsd-tests/fs/common/fstest_udf.c
@@ -0,0 +1,153 @@
+/* $NetBSD: fstest_udf.c,v 1.4 2013/07/02 15:00:55 reinoud Exp $ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nicolas Joly.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <atf-c.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <fs/udf/udf_mount.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_fsmacros.h"
+
+struct udftestargs {
+ struct udf_args ta_uargs;
+ char ta_devpath[MAXPATHLEN];
+ char ta_imgpath[MAXPATHLEN];
+};
+
+int
+udf_fstest_newfs(const atf_tc_t *tc, void **buf, const char *image, off_t size,
+ void *fspriv)
+{
+ char cmd[1024];
+ int res;
+ static unsigned int num = 0;
+ struct udftestargs *args;
+ struct sigaction act, oact;
+
+ /*
+ * Sectorsize can be specified with -S, as a multiple of 512.
+ * newfs_udf takes humanized number as size in bytes as -s parameter!
+ */
+ snprintf(cmd, 1024, "newfs_udf -F -s %"PRId64" %s >/dev/null", size, image);
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = SIG_DFL;
+ sigaction(SIGCHLD, &act, &oact);
+ res = system(cmd);
+ sigaction(SIGCHLD, &oact, NULL);
+ if (res != 0)
+ return res;
+
+ res = rump_init();
+ if (res != 0)
+ return res;
+
+ args = calloc(1, sizeof(*args));
+ if (args == NULL)
+ return -1;
+
+ snprintf(args->ta_devpath, MAXPATHLEN, "/dev/device%d.udf", num);
+ snprintf(args->ta_imgpath, MAXPATHLEN, "%s", image);
+ args->ta_uargs.fspec = args->ta_devpath;
+ args->ta_uargs.version = UDFMNT_VERSION;
+
+ res = rump_pub_etfs_register(args->ta_devpath, image, RUMP_ETFS_BLK);
+ if (res != 0) {
+ free(args);
+ return res;
+ }
+
+ *buf = args;
+ num++;
+
+ return 0;
+}
+__strong_alias(udflog_fstest_newfs,udf_fstest_newfs);
+
+int
+udf_fstest_delfs(const atf_tc_t *tc, void *buf)
+{
+ int res;
+ struct udftestargs *args = buf;
+
+ res = rump_pub_etfs_remove(args->ta_devpath);
+ if (res != 0) {
+ errno = res;
+ return -1;
+ }
+
+ res = unlink(args->ta_imgpath);
+ if (res != 0)
+ return res;
+
+ free(args);
+
+ return 0;
+}
+__strong_alias(udflog_fstest_delfs,udf_fstest_delfs);
+
+int
+udf_fstest_mount(const atf_tc_t *tc, void *buf, const char *path, int flags)
+{
+ int res;
+ struct udftestargs *args = buf;
+
+ res = rump_sys_mkdir(path, 0777);
+ if (res == -1)
+ return res;
+
+ res = rump_sys_mount(MOUNT_UDF, path, flags, &args->ta_uargs,
+ sizeof(args->ta_uargs));
+ return res;
+}
+
+int
+udf_fstest_unmount(const atf_tc_t *tc, const char *path, int flags)
+{
+ int res;
+
+ res = rump_sys_unmount(path, flags);
+ if (res == -1)
+ return res;
+
+ res = rump_sys_rmdir(path);
+ return res;
+}
+__strong_alias(udflog_fstest_unmount,udf_fstest_unmount);
diff --git a/contrib/netbsd-tests/fs/common/fstest_v7fs.c b/contrib/netbsd-tests/fs/common/fstest_v7fs.c
new file mode 100644
index 000000000000..92110e430e83
--- /dev/null
+++ b/contrib/netbsd-tests/fs/common/fstest_v7fs.c
@@ -0,0 +1,140 @@
+/* $NetBSD: fstest_v7fs.c,v 1.1 2011/08/11 10:52:12 uch Exp $ */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nicolas Joly.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <atf-c.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <fs/v7fs/v7fs_args.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_fsmacros.h"
+
+struct v7fstestargs {
+ struct v7fs_args ta_uargs;
+ char ta_devpath[MAXPATHLEN];
+ char ta_imgpath[MAXPATHLEN];
+};
+
+int
+v7fs_fstest_newfs(const atf_tc_t *tc, void **buf, const char *image,
+ off_t size, void *fspriv)
+{
+ char cmd[1024];
+ int res;
+ static unsigned int num = 0;
+ struct v7fstestargs *args;
+
+ size /= 512;
+ snprintf(cmd, 1024, "newfs_v7fs -F -s %"PRId64" %s >/dev/null",
+ size, image);
+ res = system(cmd);
+ if (res != 0)
+ return res;
+
+ res = rump_init();
+ if (res != 0)
+ return res;
+
+ args = calloc(1, sizeof(*args));
+ if (args == NULL)
+ return -1;
+
+ snprintf(args->ta_devpath, MAXPATHLEN, "/dev/device%d.v7fs", num);
+ snprintf(args->ta_imgpath, MAXPATHLEN, "%s", image);
+ args->ta_uargs.fspec = args->ta_devpath;
+ args->ta_uargs.endian = _BYTE_ORDER;
+
+ res = rump_pub_etfs_register(args->ta_devpath, image, RUMP_ETFS_BLK);
+ if (res != 0) {
+ free(args);
+ return res;
+ }
+
+ *buf = args;
+ num++;
+
+ return 0;
+}
+
+int
+v7fs_fstest_delfs(const atf_tc_t *tc, void *buf)
+{
+ int res;
+ struct v7fstestargs *args = buf;
+
+ res = rump_pub_etfs_remove(args->ta_devpath);
+ if (res != 0)
+ return res;
+
+ res = unlink(args->ta_imgpath);
+ if (res != 0)
+ return res;
+
+ free(args);
+
+ return 0;
+}
+
+int
+v7fs_fstest_mount(const atf_tc_t *tc, void *buf, const char *path, int flags)
+{
+ int res;
+ struct v7fstestargs *args = buf;
+
+ res = rump_sys_mkdir(path, 0777);
+ if (res == -1)
+ return res;
+
+ res = rump_sys_mount(MOUNT_V7FS, path, flags, &args->ta_uargs,
+ sizeof(args->ta_uargs));
+ return res;
+}
+
+int
+v7fs_fstest_unmount(const atf_tc_t *tc, const char *path, int flags)
+{
+ int res;
+
+ res = rump_sys_unmount(path, flags);
+ if (res == -1)
+ return res;
+
+ res = rump_sys_rmdir(path);
+ return res;
+}
diff --git a/contrib/netbsd-tests/fs/common/fstest_zfs.c b/contrib/netbsd-tests/fs/common/fstest_zfs.c
new file mode 100644
index 000000000000..88aa05f28ff7
--- /dev/null
+++ b/contrib/netbsd-tests/fs/common/fstest_zfs.c
@@ -0,0 +1,134 @@
+/* $NetBSD: fstest_zfs.c,v 1.1 2012/08/20 16:37:35 pooka Exp $ */
+
+/*-
+ * Copyright (c) 2010, 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nicolas Joly.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <atf-c.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_fsmacros.h"
+
+#define SRVPATH "zfssurvo"
+#define SRVURL "unix://" SRVPATH
+#define ZFSDEV "/zfsdev"
+
+int
+zfs_fstest_newfs(const atf_tc_t *tc, void **buf, const char *image,
+ off_t size, void *fspriv)
+{
+ int res;
+ int fd;
+
+ /* XXX: hardcoded zfs minimum size */
+ size = MAX(64*1024*1024, size);
+
+ res = rump_init();
+ if (res != 0) {
+ errno = res;
+ return -1;
+ }
+
+ /* create backing image, sparse file is enough */
+ if ((fd = open(image, O_RDWR | O_CREAT, 0777)) == -1)
+ return -1;
+ if (ftruncate(fd, size) == -1) {
+ close(fd);
+ return -1;
+ }
+ close(fd);
+
+ res = rump_pub_etfs_register(ZFSDEV, image, RUMP_ETFS_BLK);
+ if (res != 0) {
+ errno = res;
+ return -1;
+ }
+
+ res = rump_init_server(SRVURL);
+ if (res != 0) {
+ errno = res;
+ return -1;
+ }
+ *buf = NULL;
+
+ return 0;
+}
+
+int
+zfs_fstest_delfs(const atf_tc_t *tc, void *buf)
+{
+
+ unlink(SRVPATH);
+ return 0;
+}
+
+int
+zfs_fstest_mount(const atf_tc_t *tc, void *buf, const char *path, int flags)
+{
+ char tmpbuf[128];
+ int error;
+
+ /* set up the hijack env for running zpool */
+ setenv("RUMP_SERVER", SRVURL, 1);
+ snprintf(tmpbuf, sizeof(tmpbuf)-1, "blanket=/dev/zfs:%s:%s",
+ ZFSDEV, path);
+ setenv("RUMPHIJACK", tmpbuf, 1);
+ setenv("LD_PRELOAD", "/usr/lib/librumphijack.so", 1);
+
+ while (*path == '/')
+ path++;
+
+ /* run zpool create */
+ snprintf(tmpbuf, sizeof(tmpbuf)-1, "zpool create %s %s",
+ path, ZFSDEV);
+ if ((error = system(tmpbuf)) != 0) {
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+zfs_fstest_unmount(const atf_tc_t *tc, const char *path, int flags)
+{
+
+ unmount(path, flags);
+ unlink(SRVPATH);
+
+ return 0;
+}
diff --git a/contrib/netbsd-tests/fs/common/h_fsmacros.h b/contrib/netbsd-tests/fs/common/h_fsmacros.h
new file mode 100644
index 000000000000..eb8376873b2f
--- /dev/null
+++ b/contrib/netbsd-tests/fs/common/h_fsmacros.h
@@ -0,0 +1,329 @@
+/* $NetBSD: h_fsmacros.h,v 1.41 2017/01/13 21:30:39 christos Exp $ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nicolas Joly.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __H_FSMACROS_H_
+#define __H_FSMACROS_H_
+
+#include <sys/mount.h>
+
+#include <atf-c.h>
+#include <puffsdump.h>
+#include <string.h>
+
+#include <rump/rump.h>
+
+#include "h_macros.h"
+
+#define FSPROTOS(_fs_) \
+int _fs_##_fstest_newfs(const atf_tc_t *, void **, const char *, \
+ off_t, void *); \
+int _fs_##_fstest_delfs(const atf_tc_t *, void *); \
+int _fs_##_fstest_mount(const atf_tc_t *, void *, const char *, int); \
+int _fs_##_fstest_unmount(const atf_tc_t *, const char *, int);
+
+FSPROTOS(ext2fs);
+FSPROTOS(ffs);
+FSPROTOS(ffslog);
+FSPROTOS(lfs);
+FSPROTOS(msdosfs);
+FSPROTOS(nfs);
+FSPROTOS(nfsro);
+FSPROTOS(p2k_ffs);
+FSPROTOS(puffs);
+FSPROTOS(rumpfs);
+FSPROTOS(sysvbfs);
+FSPROTOS(tmpfs);
+FSPROTOS(udf);
+FSPROTOS(v7fs);
+FSPROTOS(zfs);
+
+#ifndef FSTEST_IMGNAME
+#define FSTEST_IMGNAME "image.fs"
+#endif
+#ifndef FSTEST_IMGSIZE
+#define FSTEST_IMGSIZE (10000 * 512)
+#endif
+#ifndef FSTEST_MNTNAME
+#define FSTEST_MNTNAME "/mnt"
+#endif
+
+#define FSTEST_CONSTRUCTOR(_tc_, _fs_, _args_) \
+do { \
+ if (_fs_##_fstest_newfs(_tc_, &_args_, \
+ FSTEST_IMGNAME, FSTEST_IMGSIZE, NULL) != 0) \
+ atf_tc_fail_errno("newfs failed"); \
+ if (_fs_##_fstest_mount(_tc_, _args_, FSTEST_MNTNAME, 0) != 0) \
+ atf_tc_fail_errno("mount failed"); \
+} while (/*CONSTCOND*/0);
+
+#define FSTEST_CONSTRUCTOR_FSPRIV(_tc_, _fs_, _args_, _privargs_) \
+do { \
+ if (_fs_##_fstest_newfs(_tc_, &_args_, \
+ FSTEST_IMGNAME, FSTEST_IMGSIZE, _privargs_) != 0) \
+ atf_tc_fail_errno("newfs failed"); \
+ if (_fs_##_fstest_mount(_tc_, _args_, FSTEST_MNTNAME, 0) != 0) \
+ atf_tc_fail_errno("mount failed"); \
+} while (/*CONSTCOND*/0);
+
+#define FSTEST_DESTRUCTOR(_tc_, _fs_, _args_) \
+do { \
+ if (_fs_##_fstest_unmount(_tc_, FSTEST_MNTNAME, 0) != 0) { \
+ rump_pub_vfs_mount_print(FSTEST_MNTNAME, 1); \
+ atf_tc_fail_errno("unmount failed"); \
+ } \
+ if (_fs_##_fstest_delfs(_tc_, _args_) != 0) \
+ atf_tc_fail_errno("delfs failed"); \
+} while (/*CONSTCOND*/0);
+
+#define ATF_TC_FSADD(fs,type,func,desc) \
+ ATF_TC(fs##_##func); \
+ ATF_TC_HEAD(fs##_##func,tc) \
+ { \
+ atf_tc_set_md_var(tc, "descr", type " test for " desc); \
+ atf_tc_set_md_var(tc, "X-fs.type", #fs); \
+ atf_tc_set_md_var(tc, "X-fs.mntname", type); \
+ } \
+ void *fs##func##tmp; \
+ \
+ ATF_TC_BODY(fs##_##func,tc) \
+ { \
+ if (!atf_check_fstype(tc, #fs)) \
+ atf_tc_skip("filesystem not selected"); \
+ FSTEST_CONSTRUCTOR(tc,fs,fs##func##tmp); \
+ func(tc,FSTEST_MNTNAME); \
+ if (fs##_fstest_unmount(tc, FSTEST_MNTNAME, 0) != 0) { \
+ rump_pub_vfs_mount_print(FSTEST_MNTNAME, 1); \
+ atf_tc_fail_errno("unmount failed"); \
+ } \
+ }
+
+#define ATF_TC_FSADD_RO(_fs_,_type_,_func_,_desc_,_gen_) \
+ ATF_TC(_fs_##_##_func_); \
+ ATF_TC_HEAD(_fs_##_##_func_,tc) \
+ { \
+ atf_tc_set_md_var(tc, "descr",_type_" test for "_desc_);\
+ atf_tc_set_md_var(tc, "X-fs.type", #_fs_); \
+ atf_tc_set_md_var(tc, "X-fs.mntname", _type_); \
+ } \
+ void *_fs_##_func_##tmp; \
+ \
+ ATF_TC_BODY(_fs_##_##_func_,tc) \
+ { \
+ if (!atf_check_fstype(tc, #_fs_)) \
+ atf_tc_skip("filesystem not selected"); \
+ FSTEST_CONSTRUCTOR(tc,_fs_,_fs_##_func_##tmp); \
+ _gen_(tc,FSTEST_MNTNAME); \
+ if (_fs_##_fstest_unmount(tc, FSTEST_MNTNAME, 0) != 0) \
+ atf_tc_fail_errno("unmount r/w failed"); \
+ if (_fs_##_fstest_mount(tc, _fs_##_func_##tmp, \
+ FSTEST_MNTNAME, MNT_RDONLY) != 0) \
+ atf_tc_fail_errno("mount ro failed"); \
+ _func_(tc,FSTEST_MNTNAME); \
+ if (_fs_##_fstest_unmount(tc, FSTEST_MNTNAME, 0) != 0) {\
+ rump_pub_vfs_mount_print(FSTEST_MNTNAME, 1); \
+ atf_tc_fail_errno("unmount failed"); \
+ } \
+ }
+
+#define ATF_TP_FSADD(fs,func) \
+ ATF_TP_ADD_TC(tp,fs##_##func)
+
+#define ATF_TC_FSAPPLY_NOZFS(func,desc) \
+ ATF_TC_FSADD(ext2fs,MOUNT_EXT2FS,func,desc) \
+ ATF_TC_FSADD(ffs,MOUNT_FFS,func,desc) \
+ ATF_TC_FSADD(ffslog,MOUNT_FFS,func,desc) \
+ ATF_TC_FSADD(lfs,MOUNT_LFS,func,desc) \
+ ATF_TC_FSADD(msdosfs,MOUNT_MSDOS,func,desc) \
+ ATF_TC_FSADD(nfs,MOUNT_NFS,func,desc) \
+ ATF_TC_FSADD(puffs,MOUNT_PUFFS,func,desc) \
+ ATF_TC_FSADD(p2k_ffs,MOUNT_PUFFS,func,desc) \
+ ATF_TC_FSADD(rumpfs,MOUNT_RUMPFS,func,desc) \
+ ATF_TC_FSADD(sysvbfs,MOUNT_SYSVBFS,func,desc) \
+ ATF_TC_FSADD(tmpfs,MOUNT_TMPFS,func,desc) \
+ ATF_TC_FSADD(udf,MOUNT_UDF,func,desc) \
+ ATF_TC_FSADD(v7fs,MOUNT_V7FS,func,desc)
+
+#define ATF_TP_FSAPPLY_NOZFS(func) \
+ ATF_TP_FSADD(ext2fs,func); \
+ ATF_TP_FSADD(ffs,func); \
+ ATF_TP_FSADD(ffslog,func); \
+ ATF_TP_FSADD(lfs,func); \
+ ATF_TP_FSADD(msdosfs,func); \
+ ATF_TP_FSADD(nfs,func); \
+ ATF_TP_FSADD(puffs,func); \
+ ATF_TP_FSADD(p2k_ffs,func); \
+ ATF_TP_FSADD(rumpfs,func); \
+ ATF_TP_FSADD(sysvbfs,func); \
+ ATF_TP_FSADD(tmpfs,func); \
+ ATF_TP_FSADD(udf,func); \
+ ATF_TP_FSADD(v7fs,func);
+
+/* XXX: this will not scale */
+#ifdef WANT_ZFS_TESTS
+#define ATF_TC_FSAPPLY(func,desc) \
+ ATF_TC_FSAPPLY_NOZFS(func,desc) \
+ ATF_TC_FSADD(zfs,MOUNT_ZFS,func,desc)
+#define ATF_TP_FSAPPLY(func) \
+ ATF_TP_FSAPPLY_NOZFS(func) \
+ ATF_TP_FSADD(zfs,func);
+
+#else /* !WANT_ZFS_TESTS */
+
+#define ATF_TC_FSAPPLY(func,desc) \
+ ATF_TC_FSAPPLY_NOZFS(func,desc)
+#define ATF_TP_FSAPPLY(func) \
+ ATF_TP_FSAPPLY_NOZFS(func)
+
+#endif /* WANT_ZFS_TESTS */
+
+/*
+ * Same as above, but generate a file system image first and perform
+ * tests for a r/o mount.
+ *
+ * Missing the following file systems:
+ * + lfs (fstest_lfs routines cannot handle remount. FIXME!)
+ * + tmpfs (memory backend)
+ * + rumpfs (memory backend)
+ * + puffs (memory backend, but could be run in theory)
+ */
+
+#define ATF_TC_FSAPPLY_RO(func,desc,gen) \
+ ATF_TC_FSADD_RO(ext2fs,MOUNT_EXT2FS,func,desc,gen) \
+ ATF_TC_FSADD_RO(ffs,MOUNT_FFS,func,desc,gen) \
+ ATF_TC_FSADD_RO(ffslog,MOUNT_FFS,func,desc,gen) \
+ ATF_TC_FSADD_RO(msdosfs,MOUNT_MSDOS,func,desc,gen) \
+ ATF_TC_FSADD_RO(nfs,MOUNT_NFS,func,desc,gen) \
+ ATF_TC_FSADD_RO(nfsro,MOUNT_NFS,func,desc,gen) \
+ ATF_TC_FSADD_RO(sysvbfs,MOUNT_SYSVBFS,func,desc,gen) \
+ ATF_TC_FSADD_RO(udf,MOUNT_UDF,func,desc,gen) \
+ ATF_TC_FSADD_RO(v7fs,MOUNT_V7FS,func,desc,gen)
+
+#define ATF_TP_FSAPPLY_RO(func) \
+ ATF_TP_FSADD(ext2fs,func); \
+ ATF_TP_FSADD(ffs,func); \
+ ATF_TP_FSADD(ffslog,func); \
+ ATF_TP_FSADD(msdosfs,func); \
+ ATF_TP_FSADD(nfs,func); \
+ ATF_TP_FSADD(nfsro,func); \
+ ATF_TP_FSADD(sysvbfs,func); \
+ ATF_TP_FSADD(udf,func); \
+ ATF_TP_FSADD(v7fs,func);
+
+#define ATF_FSAPPLY(func,desc) \
+ ATF_TC_FSAPPLY(func,desc); \
+ ATF_TP_ADD_TCS(tp) \
+ { \
+ ATF_TP_FSAPPLY(func); \
+ return atf_no_error(); \
+ }
+
+static __inline bool
+atf_check_fstype(const atf_tc_t *tc, const char *fs)
+{
+ const char *fstype;
+
+ if (!atf_tc_has_config_var(tc, "fstype"))
+ return true;
+
+ fstype = atf_tc_get_config_var(tc, "fstype");
+ if (strcmp(fstype, fs) == 0)
+ return true;
+ return false;
+}
+
+#define FSTYPE_EXT2FS(tc)\
+ (strcmp(atf_tc_get_md_var(tc, "X-fs.type"), "ext2fs") == 0)
+#define FSTYPE_FFS(tc)\
+ (strcmp(atf_tc_get_md_var(tc, "X-fs.type"), "ffs") == 0)
+#define FSTYPE_FFSLOG(tc)\
+ (strcmp(atf_tc_get_md_var(tc, "X-fs.type"), "ffslog") == 0)
+#define FSTYPE_LFS(tc)\
+ (strcmp(atf_tc_get_md_var(tc, "X-fs.type"), "lfs") == 0)
+#define FSTYPE_MSDOS(tc)\
+ (strcmp(atf_tc_get_md_var(tc, "X-fs.type"), "msdosfs") == 0)
+#define FSTYPE_NFS(tc)\
+ (strcmp(atf_tc_get_md_var(tc, "X-fs.type"), "nfs") == 0)
+#define FSTYPE_NFSRO(tc)\
+ (strcmp(atf_tc_get_md_var(tc, "X-fs.type"), "nfsro") == 0)
+#define FSTYPE_P2K_FFS(tc)\
+ (strcmp(atf_tc_get_md_var(tc, "X-fs.type"), "p2k_ffs") == 0)
+#define FSTYPE_PUFFS(tc)\
+ (strcmp(atf_tc_get_md_var(tc, "X-fs.type"), "puffs") == 0)
+#define FSTYPE_RUMPFS(tc)\
+ (strcmp(atf_tc_get_md_var(tc, "X-fs.type"), "rumpfs") == 0)
+#define FSTYPE_SYSVBFS(tc)\
+ (strcmp(atf_tc_get_md_var(tc, "X-fs.type"), "sysvbfs") == 0)
+#define FSTYPE_TMPFS(tc)\
+ (strcmp(atf_tc_get_md_var(tc, "X-fs.type"), "tmpfs") == 0)
+#define FSTYPE_UDF(tc)\
+ (strcmp(atf_tc_get_md_var(tc, "X-fs.type"), "udf") == 0)
+#define FSTYPE_V7FS(tc)\
+ (strcmp(atf_tc_get_md_var(tc, "X-fs.type"), "v7fs") == 0)
+#define FSTYPE_ZFS(tc)\
+ (strcmp(atf_tc_get_md_var(tc, "X-fs.type"), "zfs") == 0)
+
+#define FSTEST_ENTER() \
+ if (rump_sys_chdir(FSTEST_MNTNAME) == -1) \
+ atf_tc_fail_errno("failed to cd into test mount")
+
+#define FSTEST_EXIT() \
+ if (rump_sys_chdir("/") == -1) \
+ atf_tc_fail_errno("failed to cd out of test mount")
+
+/*
+ * file system args structures
+ */
+
+struct nfstestargs {
+ pid_t ta_childpid;
+ char ta_ethername[MAXPATHLEN];
+};
+
+struct puffstestargs {
+ uint8_t *pta_pargs;
+ size_t pta_pargslen;
+
+ int pta_pflags;
+ pid_t pta_childpid;
+
+ int pta_rumpfd;
+ int pta_servfd;
+
+ char pta_dev[MAXPATHLEN];
+ char pta_dir[MAXPATHLEN];
+
+ int pta_mntflags;
+
+ int pta_vfs_toserv_ops[PUFFS_VFS_MAX];
+ int pta_vn_toserv_ops[PUFFS_VN_MAX];
+};
+
+#endif /* __H_FSMACROS_H_ */
diff --git a/contrib/netbsd-tests/fs/common/snapshot.c b/contrib/netbsd-tests/fs/common/snapshot.c
new file mode 100644
index 000000000000..7baf611b6ed6
--- /dev/null
+++ b/contrib/netbsd-tests/fs/common/snapshot.c
@@ -0,0 +1,228 @@
+/* $NetBSD: snapshot.c,v 1.7 2013/02/06 09:05:01 hannken Exp $ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+
+#include <dev/fssvar.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+ATF_TC_WITH_CLEANUP(snapshot);
+ATF_TC_HEAD(snapshot, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "basic snapshot features");
+}
+
+static void
+makefile(const char *path)
+{
+ int fd;
+
+ fd = rump_sys_open(path, O_CREAT | O_RDWR, 0777);
+ if (fd == -1)
+ atf_tc_fail_errno("create %s", path);
+ rump_sys_close(fd);
+}
+
+ATF_TC_BODY(snapshot, tc)
+{
+ char buf[1024];
+ struct fss_set fss;
+ int fssfd;
+ int fd, fd2, i;
+
+ if (system(NEWFS) == -1)
+ atf_tc_fail_errno("cannot create file system");
+
+ rump_init();
+ begin();
+
+ if (rump_sys_mkdir("/mnt", 0777) == -1)
+ atf_tc_fail_errno("mount point create");
+ if (rump_sys_mkdir("/snap", 0777) == -1)
+ atf_tc_fail_errno("mount point 2 create");
+
+ rump_pub_etfs_register("/diskdev", IMGNAME, RUMP_ETFS_BLK);
+
+ mount_diskfs("/diskdev", "/mnt");
+
+#define TESTSTR1 "huihai\n"
+#define TESTSZ1 (sizeof(TESTSTR1)-1)
+#define TESTSTR2 "baana liten\n"
+#define TESTSZ2 (sizeof(TESTSTR2)-1)
+
+ fd = rump_sys_open("/mnt/myfile", O_RDWR | O_CREAT, 0777);
+ if (fd == -1)
+ atf_tc_fail_errno("create file");
+ if (rump_sys_write(fd, TESTSTR1, TESTSZ1) != TESTSZ1)
+ atf_tc_fail_errno("write fail");
+
+ fssfd = rump_sys_open("/dev/rfss0", O_RDWR);
+ if (fssfd == -1)
+ atf_tc_fail_errno("cannot open fss");
+ makefile(BAKNAME);
+ memset(&fss, 0, sizeof(fss));
+ fss.fss_mount = __UNCONST("/mnt");
+ fss.fss_bstore = __UNCONST(BAKNAME);
+ fss.fss_csize = 0;
+ if (rump_sys_ioctl(fssfd, FSSIOCSET, &fss) == -1)
+ atf_tc_fail_errno("create snapshot");
+
+ for (i = 0; i < 10000; i++) {
+ if (rump_sys_write(fd, TESTSTR2, TESTSZ2) != TESTSZ2)
+ atf_tc_fail_errno("write fail");
+ }
+ rump_sys_sync();
+
+ /* technically we should fsck it first? */
+ mount_diskfs("/dev/fss0", "/snap");
+
+ /* check for old contents */
+ fd2 = rump_sys_open("/snap/myfile", O_RDONLY);
+ if (fd2 == -1)
+ atf_tc_fail_errno("fail");
+ memset(buf, 0, sizeof(buf));
+ if (rump_sys_read(fd2, buf, sizeof(buf)) == -1)
+ atf_tc_fail_errno("read snap");
+ ATF_CHECK(strcmp(buf, TESTSTR1) == 0);
+
+ /* check that new files are invisible in the snapshot */
+ makefile("/mnt/newfile");
+ if (rump_sys_open("/snap/newfile", O_RDONLY) != -1)
+ atf_tc_fail("newfile exists in snapshot");
+ if (errno != ENOENT)
+ atf_tc_fail_errno("newfile open should fail with ENOENT");
+
+ /* check that removed files are still visible in the snapshot */
+ rump_sys_unlink("/mnt/myfile");
+ if (rump_sys_open("/snap/myfile", O_RDONLY) == -1)
+ atf_tc_fail_errno("unlinked file no longer in snapshot");
+
+ /* done for now */
+}
+
+ATF_TC_CLEANUP(snapshot, tc)
+{
+
+ unlink(IMGNAME);
+}
+
+ATF_TC_WITH_CLEANUP(snapshotstress);
+ATF_TC_HEAD(snapshotstress, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "snapshot on active file system");
+}
+
+#define NACTIVITY 4
+
+static bool activity_stop = false;
+static pid_t wrkpid;
+
+static void *
+fs_activity(void *arg)
+{
+ int di, fi;
+ char *prefix = arg, path[128];
+
+ rump_pub_lwproc_newlwp(wrkpid);
+
+ RL(rump_sys_mkdir(prefix, 0777));
+ while (! activity_stop) {
+ for (di = 0; di < 5; di++) {
+ snprintf(path, sizeof(path), "%s/d%d", prefix, di);
+ RL(rump_sys_mkdir(path, 0777));
+ for (fi = 0; fi < 5; fi++) {
+ snprintf(path, sizeof(path), "%s/d%d/f%d",
+ prefix, di, fi);
+ makefile(path);
+ }
+ }
+ for (di = 0; di < 5; di++) {
+ for (fi = 0; fi < 5; fi++) {
+ snprintf(path, sizeof(path), "%s/d%d/f%d",
+ prefix, di, fi);
+ RL(rump_sys_unlink(path));
+ }
+ snprintf(path, sizeof(path), "%s/d%d", prefix, di);
+ RL(rump_sys_rmdir(path));
+ }
+ }
+ RL(rump_sys_rmdir(prefix));
+
+ rump_pub_lwproc_releaselwp();
+
+ return NULL;
+}
+
+ATF_TC_BODY(snapshotstress, tc)
+{
+ pthread_t at[NACTIVITY];
+ struct fss_set fss;
+ char prefix[NACTIVITY][128];
+ int i, fssfd;
+
+ if (system(NEWFS) == -1)
+ atf_tc_fail_errno("cannot create file system");
+ /* Force SMP so the stress makes sense. */
+ RL(setenv("RUMP_NCPU", "4", 1));
+ RZ(rump_init());
+ /* Prepare for fsck to use the RUMP /dev/fss0. */
+ RL(rump_init_server("unix://commsock"));
+ RL(setenv("LD_PRELOAD", "/usr/lib/librumphijack.so", 1));
+ RL(setenv("RUMP_SERVER", "unix://commsock", 1));
+ RL(setenv("RUMPHIJACK", "blanket=/dev/rfss0", 1));
+ begin();
+
+ RL(rump_sys_mkdir("/mnt", 0777));
+
+ rump_pub_etfs_register("/diskdev", IMGNAME, RUMP_ETFS_BLK);
+
+ mount_diskfs("/diskdev", "/mnt");
+
+ /* Start file system activity. */
+ RL(wrkpid = rump_sys_getpid());
+ for (i = 0; i < NACTIVITY; i++) {
+ snprintf(prefix[i], sizeof(prefix[i]), "/mnt/a%d", i);
+ RL(pthread_create(&at[i], NULL, fs_activity, prefix[i]));
+ sleep(1);
+ }
+
+ fssfd = rump_sys_open("/dev/rfss0", O_RDWR);
+ if (fssfd == -1)
+ atf_tc_fail_errno("cannot open fss");
+ makefile(BAKNAME);
+ memset(&fss, 0, sizeof(fss));
+ fss.fss_mount = __UNCONST("/mnt");
+ fss.fss_bstore = __UNCONST(BAKNAME);
+ fss.fss_csize = 0;
+ if (rump_sys_ioctl(fssfd, FSSIOCSET, &fss) == -1)
+ atf_tc_fail_errno("create snapshot");
+
+ activity_stop = true;
+ for (i = 0; i < NACTIVITY; i++)
+ RL(pthread_join(at[i], NULL));
+
+ RL(system(FSCK " /dev/rfss0"));
+}
+
+ATF_TC_CLEANUP(snapshotstress, tc)
+{
+
+ unlink(IMGNAME);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, snapshot);
+ ATF_TP_ADD_TC(tp, snapshotstress);
+ return 0;
+}
diff --git a/contrib/netbsd-tests/fs/ffs/ffs_common.sh b/contrib/netbsd-tests/fs/ffs/ffs_common.sh
new file mode 100755
index 000000000000..ee94a15f994a
--- /dev/null
+++ b/contrib/netbsd-tests/fs/ffs/ffs_common.sh
@@ -0,0 +1,99 @@
+# $NetBSD: ffs_common.sh,v 1.3 2016/10/08 13:23:53 gson Exp $
+
+create_ffs()
+{
+ local endian=$1; shift
+ local vers=$1; shift
+ local type=$1; shift
+ local op;
+ if [ ${type} = "both" ]; then
+ op="-q user -q group"
+ else
+ op="-q ${type}"
+ fi
+ atf_check -o ignore -e ignore newfs ${op} \
+ -B ${endian} -O ${vers} -s 4000 -F ${IMG}
+}
+
+create_ffs_server()
+{
+ local sarg=$1; shift
+ create_ffs $*
+ atf_check -o ignore -e ignore $(atf_get_srcdir)/h_ffs_server \
+ ${sarg} ${IMG} ${RUMP_SERVER}
+}
+
+rump_shutdown()
+{
+ for s in ${RUMP_SOCKETS_LIST}; do
+ atf_check -s exit:0 env RUMP_SERVER=unix://${s} rump.halt;
+ done
+# check that the quota inode creation didn't corrupt the filesystem
+ atf_check -s exit:0 -o "match:already clean" \
+ -o "match:Phase 6 - Check Quotas" \
+ fsck_ffs -nf -F ${IMG}
+}
+
+# from tests/ipf/h_common.sh via tests/sbin/resize_ffs
+test_case()
+{
+ local name="${1}"; shift
+ local check_function="${1}"; shift
+ local descr="${1}"; shift
+
+ atf_test_case "${name}" cleanup
+
+ eval "${name}_head() { \
+ atf_set "descr" "${descr}"
+ atf_set "timeout" "120"
+ }"
+ eval "${name}_body() { \
+ RUMP_SOCKETS_LIST=\${RUMP_SOCKET}; \
+ export RUMP_SERVER=unix://\${RUMP_SOCKET}; \
+ ${check_function} " "${@}" "; \
+ }"
+ eval "${name}_cleanup() { \
+ for s in \${RUMP_SOCKETS_LIST}; do \
+ export RUMP_SERVER=unix://\${s}; \
+ atf_check -s exit:1 -o ignore -e ignore rump.halt; \
+ done; \
+ }"
+ tests="${tests} ${name}"
+}
+
+test_case_root()
+{
+ local name="${1}"; shift
+ local check_function="${1}"; shift
+ local descr="${1}"; shift
+
+ atf_test_case "${name}" cleanup
+
+ eval "${name}_head() { \
+ atf_set "descr" "${descr}"
+ atf_set "require.user" "root"
+ atf_set "timeout" "360"
+ }"
+ eval "${name}_body() { \
+ RUMP_SOCKETS_LIST=\${RUMP_SOCKET}; \
+ export RUMP_SERVER=unix://\${RUMP_SOCKET}; \
+ ${check_function} " "${@}" "; \
+ }"
+ eval "${name}_cleanup() { \
+ for s in \${RUMP_SOCKETS_LIST}; do \
+ export RUMP_SERVER=unix://\${s}; \
+ atf_check -s exit:1 -o ignore -e ignore rump.halt; \
+ done; \
+ }"
+ tests="${tests} ${name}"
+}
+
+atf_init_test_cases()
+{
+ IMG=fsimage
+ DIR=target
+ RUMP_SOCKET=test;
+ for i in ${tests}; do
+ atf_add_test_case $i
+ done
+}
diff --git a/contrib/netbsd-tests/fs/ffs/h_ffs_server.c b/contrib/netbsd-tests/fs/ffs/h_ffs_server.c
new file mode 100644
index 000000000000..dd22d9faf1d8
--- /dev/null
+++ b/contrib/netbsd-tests/fs/ffs/h_ffs_server.c
@@ -0,0 +1,113 @@
+/* $NetBSD: h_ffs_server.c,v 1.2 2012/08/24 20:25:50 jmmv Exp $ */
+
+/*
+ * rump server for advanced quota tests
+ */
+
+#include "../common/h_fsmacros.h"
+
+#include <err.h>
+#include <semaphore.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <ufs/ufs/ufsmount.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+int background = 0;
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-b] [-l] diskimage bindurl\n",
+ getprogname());
+ exit(1);
+}
+
+static void
+die(const char *reason, int error)
+{
+
+ warnx("%s: %s", reason, strerror(error));
+ if (background)
+ rump_daemonize_done(error);
+ exit(1);
+}
+
+static sem_t sigsem;
+static void
+sigreboot(int sig)
+{
+
+ sem_post(&sigsem);
+}
+
+int
+main(int argc, char **argv)
+{
+ int error;
+ struct ufs_args uargs;
+ const char *filename;
+ const char *serverurl;
+ int log = 0;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "bl")) != -1) {
+ switch(ch) {
+ case 'b':
+ background = 1;
+ break;
+ case 'l':
+ log = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ filename = argv[0];
+ serverurl = argv[1];
+
+ if (background) {
+ error = rump_daemonize_begin();
+ if (error)
+ errx(1, "rump daemonize: %s", strerror(error));
+ }
+
+ error = rump_init();
+ if (error)
+ die("rump init failed", error);
+
+ if (rump_sys_mkdir(FSTEST_MNTNAME, 0777) == -1)
+ die("mount point create", errno);
+ rump_pub_etfs_register("/diskdev", filename, RUMP_ETFS_BLK);
+ uargs.fspec = __UNCONST("/diskdev");
+ if (rump_sys_mount(MOUNT_FFS, FSTEST_MNTNAME, (log) ? MNT_LOG : 0,
+ &uargs, sizeof(uargs)) == -1)
+ die("mount ffs", errno);
+
+ error = rump_init_server(serverurl);
+ if (error)
+ die("rump server init failed", error);
+ if (background)
+ rump_daemonize_done(RUMP_DAEMONIZE_SUCCESS);
+
+ sem_init(&sigsem, 0, 0);
+ signal(SIGTERM, sigreboot);
+ signal(SIGINT, sigreboot);
+ sem_wait(&sigsem);
+
+ rump_sys_reboot(0, NULL);
+ /*NOTREACHED*/
+ return 0;
+}
diff --git a/contrib/netbsd-tests/fs/ffs/h_quota2_tests.c b/contrib/netbsd-tests/fs/ffs/h_quota2_tests.c
new file mode 100644
index 000000000000..71cf3b4958f1
--- /dev/null
+++ b/contrib/netbsd-tests/fs/ffs/h_quota2_tests.c
@@ -0,0 +1,468 @@
+/* $NetBSD: h_quota2_tests.c,v 1.5 2017/01/13 21:30:39 christos Exp $ */
+
+/*
+ * rump server for advanced quota tests
+ * this one includes functions to run against the filesystem before
+ * starting to handle rump requests from clients.
+ */
+
+#include "../common/h_fsmacros.h"
+
+#include <err.h>
+#include <semaphore.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <ufs/ufs/ufsmount.h>
+#include <dev/fssvar.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_macros.h"
+
+int background = 0;
+
+#define TEST_NONROOT_ID 1
+
+static int
+quota_test0(const char *testopts)
+{
+ static char buf[512];
+ int fd;
+ int error;
+ unsigned int i;
+ int chowner = 1;
+ for (i =0; testopts && i < strlen(testopts); i++) {
+ switch(testopts[i]) {
+ case 'C':
+ chowner = 0;
+ break;
+ default:
+ errx(1, "test4: unknown option %c", testopts[i]);
+ }
+ }
+ if (chowner)
+ rump_sys_chown(".", TEST_NONROOT_ID, TEST_NONROOT_ID);
+ rump_sys_chmod(".", 0777);
+ if (rump_sys_setegid(TEST_NONROOT_ID) != 0) {
+ error = errno;
+ warn("rump_sys_setegid");
+ return error;
+ }
+ if (rump_sys_seteuid(TEST_NONROOT_ID) != 0) {
+ error = errno;
+ warn("rump_sys_seteuid");
+ return error;
+ }
+ fd = rump_sys_open("test_fillup", O_CREAT | O_RDWR, 0644);
+ if (fd < 0) {
+ error = errno;
+ warn("rump_sys_open");
+ } else {
+ while (rump_sys_write(fd, buf, sizeof(buf)) == sizeof(buf))
+ error = 0;
+ error = errno;
+ }
+ rump_sys_close(fd);
+ rump_sys_seteuid(0);
+ rump_sys_setegid(0);
+ return error;
+}
+
+static int
+quota_test1(const char *testopts)
+{
+ static char buf[512];
+ int fd;
+ int error;
+ rump_sys_chown(".", TEST_NONROOT_ID, TEST_NONROOT_ID);
+ rump_sys_chmod(".", 0777);
+ if (rump_sys_setegid(TEST_NONROOT_ID) != 0) {
+ error = errno;
+ warn("rump_sys_setegid");
+ return error;
+ }
+ if (rump_sys_seteuid(TEST_NONROOT_ID) != 0) {
+ error = errno;
+ warn("rump_sys_seteuid");
+ return error;
+ }
+ fd = rump_sys_open("test_fillup", O_CREAT | O_RDWR, 0644);
+ if (fd < 0) {
+ error = errno;
+ warn("rump_sys_open");
+ } else {
+ /*
+ * write up to the soft limit, wait a bit, an try to
+ * keep on writing
+ */
+ int i;
+
+ /* write 2k: with the directory this makes 2.5K */
+ for (i = 0; i < 4; i++) {
+ error = rump_sys_write(fd, buf, sizeof(buf));
+ if (error != sizeof(buf))
+ err(1, "write failed early");
+ }
+ sleep(2);
+ /* now try to write an extra .5k */
+ if (rump_sys_write(fd, buf, sizeof(buf)) != sizeof(buf))
+ error = errno;
+ else
+ error = 0;
+ }
+ rump_sys_close(fd);
+ rump_sys_seteuid(0);
+ rump_sys_setegid(0);
+ return error;
+}
+
+static int
+quota_test2(const char *testopts)
+{
+ static char buf[512];
+ int fd;
+ int error;
+ int i;
+ rump_sys_chown(".", TEST_NONROOT_ID, TEST_NONROOT_ID);
+ rump_sys_chmod(".", 0777);
+ if (rump_sys_setegid(TEST_NONROOT_ID) != 0) {
+ error = errno;
+ warn("rump_sys_setegid");
+ return error;
+ }
+ if (rump_sys_seteuid(TEST_NONROOT_ID) != 0) {
+ error = errno;
+ warn("rump_sys_seteuid");
+ return error;
+ }
+
+ for (i = 0; ; i++) {
+ sprintf(buf, "file%d", i);
+ fd = rump_sys_open(buf, O_CREAT | O_RDWR, 0644);
+ if (fd < 0)
+ break;
+ sprintf(buf, "test file no %d", i);
+ rump_sys_write(fd, buf, strlen(buf));
+ rump_sys_close(fd);
+ }
+ error = errno;
+
+ rump_sys_close(fd);
+ rump_sys_seteuid(0);
+ rump_sys_setegid(0);
+ return error;
+}
+
+static int
+quota_test3(const char *testopts)
+{
+ static char buf[512];
+ int fd;
+ int error;
+ int i;
+ rump_sys_chown(".", TEST_NONROOT_ID, TEST_NONROOT_ID);
+ rump_sys_chmod(".", 0777);
+ if (rump_sys_setegid(TEST_NONROOT_ID) != 0) {
+ error = errno;
+ warn("rump_sys_setegid");
+ return error;
+ }
+ if (rump_sys_seteuid(TEST_NONROOT_ID) != 0) {
+ error = errno;
+ warn("rump_sys_seteuid");
+ return error;
+ }
+
+ /*
+ * create files one past the soft limit: one less as we already own the
+ * root directory
+ */
+ for (i = 0; i < 4; i++) {
+ sprintf(buf, "file%d", i);
+ fd = rump_sys_open(buf, O_EXCL| O_CREAT | O_RDWR, 0644);
+ if (fd < 0)
+ err(1, "file create failed early");
+ sprintf(buf, "test file no %d", i);
+ rump_sys_write(fd, buf, strlen(buf));
+ rump_sys_close(fd);
+ }
+ /* now create an extra file after grace time: this should fail */
+ sleep(2);
+ sprintf(buf, "file%d", i);
+ fd = rump_sys_open(buf, O_EXCL| O_CREAT | O_RDWR, 0644);
+ if (fd < 0)
+ error = errno;
+ else
+ error = 0;
+
+ rump_sys_close(fd);
+ rump_sys_seteuid(0);
+ rump_sys_setegid(0);
+ return error;
+}
+
+static int
+quota_test4(const char *testopts)
+{
+ static char buf[512];
+ int fd, fssfd;
+ struct fss_set fss;
+ unsigned int i;
+ int unl=0;
+ int unconf=0;
+
+ /*
+ * take an internal snapshot of the filesystem, and create a new
+ * file with some data
+ */
+ rump_sys_chown(".", 0, 0);
+ rump_sys_chmod(".", 0777);
+
+ for (i =0; testopts && i < strlen(testopts); i++) {
+ switch(testopts[i]) {
+ case 'L':
+ unl++;
+ break;
+ case 'C':
+ unconf++;
+ break;
+ default:
+ errx(1, "test4: unknown option %c", testopts[i]);
+ }
+ }
+
+ /* first create the snapshot */
+
+ fd = rump_sys_open(FSTEST_MNTNAME "/le_snap", O_CREAT | O_RDWR, 0777);
+ if (fd == -1)
+ err(1, "create " FSTEST_MNTNAME "/le_snap");
+ rump_sys_close(fd);
+ fssfd = rump_sys_open("/dev/rfss0", O_RDWR);
+ if (fssfd == -1)
+ err(1, "cannot open fss");
+ memset(&fss, 0, sizeof(fss));
+ fss.fss_mount = __UNCONST("/mnt");
+ fss.fss_bstore = __UNCONST(FSTEST_MNTNAME "/le_snap");
+ fss.fss_csize = 0;
+ if (rump_sys_ioctl(fssfd, FSSIOCSET, &fss) == -1)
+ err(1, "create snapshot");
+ if (unl) {
+ if (rump_sys_unlink(FSTEST_MNTNAME "/le_snap") == -1)
+ err(1, "unlink snapshot");
+ }
+
+ /* now create some extra files */
+
+ for (i = 0; i < 4; i++) {
+ sprintf(buf, "file%d", i);
+ fd = rump_sys_open(buf, O_EXCL| O_CREAT | O_RDWR, 0644);
+ if (fd < 0)
+ err(1, "create %s", buf);
+ sprintf(buf, "test file no %d", i);
+ rump_sys_write(fd, buf, strlen(buf));
+ rump_sys_close(fd);
+ }
+ if (unconf)
+ if (rump_sys_ioctl(fssfd, FSSIOCCLR, NULL) == -1)
+ err(1, "unconfigure snapshot");
+ return 0;
+}
+
+static int
+quota_test5(const char *testopts)
+{
+ static char buf[512];
+ int fd;
+ int remount = 0;
+ int unlnk = 0;
+ int log = 0;
+ unsigned int i;
+
+ for (i =0; testopts && i < strlen(testopts); i++) {
+ switch(testopts[i]) {
+ case 'L':
+ log++;
+ break;
+ case 'R':
+ remount++;
+ break;
+ case 'U':
+ unlnk++;
+ break;
+ default:
+ errx(1, "test4: unknown option %c", testopts[i]);
+ }
+ }
+ if (remount) {
+ struct ufs_args uargs;
+ uargs.fspec = __UNCONST("/diskdev");
+ /* remount the fs read/write */
+ if (rump_sys_mount(MOUNT_FFS, FSTEST_MNTNAME,
+ MNT_UPDATE | (log ? MNT_LOG : 0),
+ &uargs, sizeof(uargs)) == -1)
+ err(1, "mount ffs rw %s", FSTEST_MNTNAME);
+ }
+
+ if (unlnk) {
+ /*
+ * open and unlink a file
+ */
+
+ fd = rump_sys_open("unlinked_file",
+ O_EXCL| O_CREAT | O_RDWR, 0644);
+ if (fd < 0)
+ err(1, "create %s", "unlinked_file");
+ sprintf(buf, "test unlinked_file");
+ rump_sys_write(fd, buf, strlen(buf));
+ if (rump_sys_unlink("unlinked_file") == -1)
+ err(1, "unlink unlinked_file");
+ if (rump_sys_fsync(fd) == -1)
+ err(1, "fsync unlinked_file");
+ rump_sys_reboot(RUMP_RB_NOSYNC, NULL);
+ errx(1, "reboot failed");
+ return 1;
+ }
+ return 0;
+}
+
+struct quota_test {
+ int (*func)(const char *);
+ const char *desc;
+};
+
+struct quota_test quota_tests[] = {
+ { quota_test0, "write up to hard limit"},
+ { quota_test1, "write beyond the soft limit after grace time"},
+ { quota_test2, "create file up to hard limit"},
+ { quota_test3, "create file beyond the soft limit after grace time"},
+ { quota_test4, "take a snapshot and add some data"},
+ { quota_test5, "open and unlink a file"},
+};
+
+static void
+usage(void)
+{
+ unsigned int test;
+ fprintf(stderr, "usage: %s [-b] [-l] test# diskimage bindurl\n",
+ getprogname());
+ fprintf(stderr, "available tests:\n");
+ for (test = 0; test < sizeof(quota_tests) / sizeof(quota_tests[0]);
+ test++)
+ fprintf(stderr, "\t%d: %s\n", test, quota_tests[test].desc);
+ exit(1);
+}
+
+static void
+die(const char *reason, int error)
+{
+
+ warnx("%s: %s", reason, strerror(error));
+ if (background)
+ rump_daemonize_done(error);
+ exit(1);
+}
+
+static sem_t sigsem;
+static void
+sigreboot(int sig)
+{
+
+ sem_post(&sigsem);
+}
+
+int
+main(int argc, char **argv)
+{
+ int error;
+ u_long test;
+ char *end;
+ struct ufs_args uargs;
+ const char *filename;
+ const char *serverurl;
+ const char *topts = NULL;
+ int mntopts = 0;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "blo:r")) != -1) {
+ switch(ch) {
+ case 'b':
+ background = 1;
+ break;
+ case 'l':
+ mntopts |= MNT_LOG;
+ break;
+ case 'r':
+ mntopts |= MNT_RDONLY;
+ break;
+ case 'o':
+ topts = optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 3)
+ usage();
+
+ filename = argv[1];
+ serverurl = argv[2];
+
+ test = strtoul(argv[0], &end, 10);
+ if (*end != '\0') {
+ usage();
+ }
+ if (test > sizeof(quota_tests) / sizeof(quota_tests[0])) {
+ usage();
+ }
+
+ if (background) {
+ error = rump_daemonize_begin();
+ if (error)
+ errx(1, "rump daemonize: %s", strerror(error));
+ }
+
+ error = rump_init();
+ if (error)
+ die("rump init failed", error);
+
+ if (rump_sys_mkdir(FSTEST_MNTNAME, 0777) == -1)
+ err(1, "mount point create");
+ rump_pub_etfs_register("/diskdev", filename, RUMP_ETFS_BLK);
+ uargs.fspec = __UNCONST("/diskdev");
+ if (rump_sys_mount(MOUNT_FFS, FSTEST_MNTNAME, mntopts,
+ &uargs, sizeof(uargs)) == -1)
+ die("mount ffs", errno);
+
+ if (rump_sys_chdir(FSTEST_MNTNAME) == -1)
+ err(1, "cd %s", FSTEST_MNTNAME);
+ error = quota_tests[test].func(topts);
+ if (error) {
+ fprintf(stderr, " test %lu: %s returned %d: %s\n",
+ test, quota_tests[test].desc, error, strerror(error));
+ }
+ if (rump_sys_chdir("/") == -1)
+ err(1, "cd /");
+
+ error = rump_init_server(serverurl);
+ if (error)
+ die("rump server init failed", error);
+ if (background)
+ rump_daemonize_done(RUMP_DAEMONIZE_SUCCESS);
+
+ sem_init(&sigsem, 0, 0);
+ signal(SIGTERM, sigreboot);
+ signal(SIGINT, sigreboot);
+ sem_wait(&sigsem);
+
+ rump_sys_reboot(0, NULL);
+ /*NOTREACHED*/
+ return 0;
+}
diff --git a/contrib/netbsd-tests/fs/ffs/quotas_common.sh b/contrib/netbsd-tests/fs/ffs/quotas_common.sh
new file mode 100755
index 000000000000..0ad002f54ad5
--- /dev/null
+++ b/contrib/netbsd-tests/fs/ffs/quotas_common.sh
@@ -0,0 +1,12 @@
+# $NetBSD: quotas_common.sh,v 1.4 2012/01/18 20:51:23 bouyer Exp $
+
+rump_quota_shutdown()
+{
+ for s in ${RUMP_SOCKETS_LIST}; do
+ atf_check -s exit:0 env RUMP_SERVER=unix://${s} rump.halt;
+ done
+# check that the quota inode creation didn't corrupt the filesystem
+ atf_check -s exit:0 -o "match:already clean" \
+ -o "match:Phase 6 - Check Quotas" \
+ fsck_ffs -nf -F ${IMG}
+}
diff --git a/contrib/netbsd-tests/fs/ffs/t_clearquota.sh b/contrib/netbsd-tests/fs/ffs/t_clearquota.sh
new file mode 100755
index 000000000000..f62a494730cd
--- /dev/null
+++ b/contrib/netbsd-tests/fs/ffs/t_clearquota.sh
@@ -0,0 +1,91 @@
+# $NetBSD: t_clearquota.sh,v 1.4 2012/01/18 20:51:23 bouyer Exp $
+#
+# Copyright (c) 2011 Manuel Bouyer
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+for e in le be; do
+ for v in 1 2; do
+ for q in "user" "group"; do
+ test_case_root clear_${e}_${v}_${q} clear_quota \
+ "clear quota with ${q} enabled" -b ${e} ${v} ${q}
+ done
+ test_case_root clear_${e}_${v}_"both" clear_quota \
+ "clear quota with both enabled" -b ${e} ${v} "both"
+ test_case_root clear_${e}_${v}_"both_log" clear_quota \
+ "clear quota for new id with both enabled, WAPBL" -bl ${e} ${v} "both"
+ done
+done
+
+clear_quota()
+{
+ create_ffs_server $*
+ local q=$4
+ local expect
+ local fail
+ local id=1
+
+ case ${q} in
+ user)
+ expect=u
+ fail=g
+ ;;
+ group)
+ expect=g
+ fail=u
+ ;;
+ both)
+ expect="u g"
+ fail=""
+ ;;
+ *)
+ atf_fail "wrong quota type"
+ ;;
+ esac
+
+#set and check the expected quota
+ for q in ${expect} ; do
+ atf_check -s exit:0 \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt edquota -$q -s10k/20 -h40M/50k \
+ -t 2W/3D ${id}
+ atf_check -s exit:0 \
+-o "match:/mnt 0 10 40960 2weeks 0 20 51200 3days" \
+-o "match:Disk quotas for .*: $" \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt quota -${q} -v ${id}
+ done
+#now clear the quotas
+ for q in ${expect} ; do
+ atf_check -s exit:0 \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt edquota -$q -c ${id}
+ done;
+
+#check that we do not get positive reply for any quota type
+ for q in u g ; do
+ atf_check -s exit:0 -o "not-match:/mnt" \
+ -o "not-match:Disk quotas for .*: $" \
+ -o "match:Disk quotas for .*: none$" \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt quota -${q} -v ${id}
+ done
+ rump_quota_shutdown
+}
diff --git a/contrib/netbsd-tests/fs/ffs/t_fifos.c b/contrib/netbsd-tests/fs/ffs/t_fifos.c
new file mode 100644
index 000000000000..10b50bbd792d
--- /dev/null
+++ b/contrib/netbsd-tests/fs/ffs/t_fifos.c
@@ -0,0 +1,158 @@
+/* $NetBSD: t_fifos.c,v 1.6 2017/01/13 21:30:39 christos Exp $ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include <ufs/ufs/ufsmount.h>
+
+#include "h_macros.h"
+
+ATF_TC_WITH_CLEANUP(fifos);
+ATF_TC_HEAD(fifos, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "test fifo support in ffs");
+ atf_tc_set_md_var(tc, "timeout", "5");
+}
+
+#define teststr1 "raving & drooling"
+#define teststr2 "haha, charade"
+
+static void *
+w1(void *arg)
+{
+ int fd;
+
+ fd = rump_sys_open("sheep", O_WRONLY);
+ if (fd == -1)
+ atf_tc_fail_errno("w1 open");
+ if (rump_sys_write(fd, teststr1, sizeof(teststr1)) != sizeof(teststr1))
+ atf_tc_fail_errno("w1 write");
+ rump_sys_close(fd);
+
+ return NULL;
+}
+
+static void *
+w2(void *arg)
+{
+ int fd;
+
+ fd = rump_sys_open("pigs", O_WRONLY);
+ if (fd == -1)
+ atf_tc_fail_errno("w2 open");
+ if (rump_sys_write(fd, teststr2, sizeof(teststr2)) != sizeof(teststr2))
+ atf_tc_fail_errno("w2 write");
+ rump_sys_close(fd);
+
+ return NULL;
+}
+
+static void *
+r1(void *arg)
+{
+ char buf[32];
+ int fd;
+
+ fd = rump_sys_open("sheep", O_RDONLY);
+ if (fd == -1)
+ atf_tc_fail_errno("r1 open");
+ if (rump_sys_read(fd, buf, sizeof(buf)) != sizeof(teststr1))
+ atf_tc_fail_errno("r1 read");
+ rump_sys_close(fd);
+
+ if (strcmp(teststr1, buf) != 0)
+ atf_tc_fail("got invalid str, %s vs. %s", buf, teststr1);
+
+ return NULL;
+}
+
+static void *
+r2(void *arg)
+{
+ char buf[32];
+ int fd;
+
+ fd = rump_sys_open("pigs", O_RDONLY);
+ if (fd == -1)
+ atf_tc_fail_errno("r2 open");
+ if (rump_sys_read(fd, buf, sizeof(buf)) != sizeof(teststr2))
+ atf_tc_fail_errno("r2 read");
+ rump_sys_close(fd);
+
+ if (strcmp(teststr2, buf) != 0)
+ atf_tc_fail("got invalid str, %s vs. %s", buf, teststr2);
+
+ return NULL;
+}
+
+#define IMGNAME "atf.img"
+
+const char *newfs = "newfs -F -s 10000 " IMGNAME;
+#define FAKEBLK "/dev/sp00ka"
+
+ATF_TC_BODY(fifos, tc)
+{
+ struct ufs_args args;
+ pthread_t ptw1, ptw2, ptr1, ptr2;
+
+ if (system(newfs) == -1)
+ atf_tc_fail_errno("newfs failed");
+
+ memset(&args, 0, sizeof(args));
+ args.fspec = __UNCONST(FAKEBLK);
+
+ rump_init();
+ if (rump_sys_mkdir("/animals", 0777) == -1)
+ atf_tc_fail_errno("cannot create mountpoint");
+ rump_pub_etfs_register(FAKEBLK, IMGNAME, RUMP_ETFS_BLK);
+ if (rump_sys_mount(MOUNT_FFS, "/animals", 0, &args, sizeof(args))==-1)
+ atf_tc_fail_errno("rump_sys_mount failed");
+
+ /* create fifos */
+ if (rump_sys_chdir("/animals") == 1)
+ atf_tc_fail_errno("chdir");
+ if (rump_sys_mkfifo("pigs", S_IFIFO | 0777) == -1)
+ atf_tc_fail_errno("mknod1");
+ if (rump_sys_mkfifo("sheep", S_IFIFO | 0777) == -1)
+ atf_tc_fail_errno("mknod2");
+
+ pthread_create(&ptw1, NULL, w1, NULL);
+ pthread_create(&ptw2, NULL, w2, NULL);
+ pthread_create(&ptr1, NULL, r1, NULL);
+ pthread_create(&ptr2, NULL, r2, NULL);
+
+ pthread_join(ptw1, NULL);
+ pthread_join(ptw2, NULL);
+ pthread_join(ptr1, NULL);
+ pthread_join(ptr2, NULL);
+
+ if (rump_sys_chdir("/") == 1)
+ atf_tc_fail_errno("chdir");
+
+ if (rump_sys_unmount("/animals", 0) == -1)
+ atf_tc_fail_errno("unmount failed");
+}
+
+ATF_TC_CLEANUP(fifos, tc)
+{
+
+ unlink(IMGNAME);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, fifos);
+ return 0;
+}
diff --git a/contrib/netbsd-tests/fs/ffs/t_getquota.sh b/contrib/netbsd-tests/fs/ffs/t_getquota.sh
new file mode 100755
index 000000000000..80f3cc7a6e76
--- /dev/null
+++ b/contrib/netbsd-tests/fs/ffs/t_getquota.sh
@@ -0,0 +1,112 @@
+# $NetBSD: t_getquota.sh,v 1.4 2012/01/18 20:51:23 bouyer Exp $
+#
+# Copyright (c) 2011 Manuel Bouyer
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+for e in le be; do
+ for v in 1 2; do
+ for q in "user" "group"; do
+ test_case get_${e}_${v}_${q} get_quota \
+ "get quota with ${q} enabled" -b ${e} ${v} ${q}
+ done
+ test_case get_${e}_${v}_"both" get_quota \
+ "get quota with both enabled" -b ${e} ${v} "both"
+ done
+done
+
+get_quota()
+{
+ create_ffs_server $*
+ local q=$4
+ local expect
+ local fail
+
+ case ${q} in
+ user)
+ expect=u
+ fail=g
+ ;;
+ group)
+ expect=g
+ fail=u
+ ;;
+ both)
+ expect="u g"
+ fail=""
+ ;;
+ *)
+ atf_fail "wrong quota type"
+ ;;
+ esac
+
+#check that we can get the expected quota
+ for q in ${expect} ; do
+ atf_check -s exit:0 \
+-o "match:/mnt 0 - - 7days 1 - - 7days" \
+-o "match:Disk quotas for .*: $" \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt quota -${q} -v
+ atf_check -s exit:0 \
+-o "match:-- 0 - - 1 - -" \
+-o "not-match:\+\+" \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt repquota -${q} /mnt
+ done
+
+#check that we do not get positive reply for non-expected quota
+ for q in ${fail} ; do
+ atf_check -s exit:0 -o "not-match:/mnt" \
+ -o "not-match:Disk quotas for .*: $" \
+ -o "match:Disk quotas for .*: none$" \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt quota -${q} -v
+ atf_check -s exit:0 \
+-o "not-match:-- 0 - - 1 - -" \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt repquota -${q} /mnt
+ done
+ rump_quota_shutdown
+}
+
+quota_walk_list()
+{
+ create_ffs_server $*
+ local q=$4
+ local expect
+
+ case ${q} in
+ user)
+ expect=u
+ fail=g
+ ;;
+ group)
+ expect=g
+ fail=u
+ ;;
+ both)
+ expect="u g"
+ fail=""
+ ;;
+ *)
+ atf_fail "wrong quota type"
+ ;;
+ esac
+}
diff --git a/contrib/netbsd-tests/fs/ffs/t_miscquota.sh b/contrib/netbsd-tests/fs/ffs/t_miscquota.sh
new file mode 100755
index 000000000000..904ea37f8f6c
--- /dev/null
+++ b/contrib/netbsd-tests/fs/ffs/t_miscquota.sh
@@ -0,0 +1,213 @@
+# $NetBSD: t_miscquota.sh,v 1.8 2013/01/22 06:24:11 dholland Exp $
+#
+# Copyright (c) 2011 Manuel Bouyer
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+test_case_root walk_list_user quota_walk_list \
+ "walk user quota list over several disk blocks" -b le 1 user
+
+test_case_root psnapshot_user quota_snap \
+ "create a persistent shapshot of quota-enabled fs, and do some writes" \
+ -b le 1 user
+
+test_case_root npsnapshot_user quota_snap \
+ "create a non-persistent shapshot of quota-enabled fs, and do some writes" \
+ -boL le 1 user
+
+test_case_root psnapshot_unconf_user quota_snap \
+ "create a persistent shapshot of quota-enabled fs, and do some writes and unconf" \
+ -boC le 1 user
+
+test_case_root npsnapshot_unconf_user quota_snap \
+ "create a non-persistent shapshot of quota-enabled fs, and do some writes and unconf" \
+ -boLC le 1 user
+
+test_case log_unlink quota_log \
+ "an unlinked file cleaned by the log replay should update quota" \
+ -l le 1 user
+
+test_case log_unlink_remount quota_log \
+ "an unlinked file cleaned by the log replay after remount" \
+ -oRL le 1 user
+
+
+test_case_root default_deny_user quota_default_deny \
+ "new quota entry denied by default entry" 5 -b le 1 user
+
+test_case_root default_deny_user_big quota_default_deny \
+ "new quota entry denied by default entry, with list on more than one block" 5000 -b le 1 user
+
+
+quota_walk_list()
+{
+ create_ffs_server $*
+ local q=$4
+ local expect
+
+ case ${q} in
+ user)
+ expect=u
+ fail=g
+ ;;
+ group)
+ expect=g
+ fail=u
+ ;;
+ *)
+ atf_fail "wrong quota type"
+ ;;
+ esac
+
+ # create 100 users, all in the same hash list
+ local i=1;
+ while [ $i -lt 101 ]; do
+ atf_check -s exit:0 \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt edquota -${expect} \
+ -s10k/20 -h40M/50k -t 2W/3D $((i * 4096))
+ i=$((i + 1))
+ done
+ # do a repquota
+ atf_check -s exit:0 -o 'match:user 409600 block *81920 20 0' \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt repquota -x -${expect} /mnt
+ rump_quota_shutdown
+}
+
+quota_snap()
+{
+ local flag=$1; shift
+ create_ffs $*
+ local q=$3
+ local expect
+
+ case ${q} in
+ user)
+ expect=u
+ fail=g
+ ;;
+ group)
+ expect=g
+ fail=u
+ ;;
+ *)
+ atf_fail "wrong quota type"
+ ;;
+ esac
+
+ #start our server which takes a snapshot
+ atf_check -s exit:0 -o ignore \
+ $(atf_get_srcdir)/h_quota2_tests ${flag} 4 ${IMG} ${RUMP_SERVER}
+ # create a few users
+ local i=1;
+ while [ $i -lt 11 ]; do
+ atf_check -s exit:0 \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt edquota -${expect} \
+ -s10k/20 -h40M/50k -t 2W/3D $i
+ i=$((i + 1))
+ done
+ # we should have 5 files (root + 4 regular files)
+ atf_check -s exit:0 \
+ -o 'match:- - 7days 5 - - 7days' \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt repquota -av
+ #shutdown and check filesystem
+ rump_quota_shutdown
+}
+
+quota_log()
+{
+ local srv2args=$1; shift
+ create_ffs $*
+ local q=$3
+ local expect
+
+ case ${q} in
+ user)
+ expect=u
+ fail=g
+ ;;
+ group)
+ expect=g
+ fail=u
+ ;;
+ *)
+ atf_fail "wrong quota type"
+ ;;
+ esac
+
+ #start our server which create a file and unlink while keeping
+ # it open. The server halts itself without flush
+ atf_check -s exit:0 -o ignore \
+ $(atf_get_srcdir)/h_quota2_tests -loU 5 ${IMG} ${RUMP_SERVER}
+ # we should have one unlinked file, but the log covers it.
+ atf_check -s exit:0 -o match:'3 files' -e ignore \
+ fsck_ffs -nf -F ${IMG}
+ # have a kernel mount the fs again; it should cleanup the
+ # unlinked file
+ atf_check -o ignore -e ignore $(atf_get_srcdir)/h_quota2_tests \
+ ${srv2args} -b 5 ${IMG} ${RUMP_SERVER}
+ #shutdown and check filesystem
+ rump_quota_shutdown
+}
+
+quota_default_deny()
+{
+ local nusers=$1; shift
+ create_ffs_server $*
+ local q=$4
+ local expect
+
+ case ${q} in
+ user)
+ expect=u
+ fail=g
+ ;;
+ group)
+ expect=g
+ fail=u
+ ;;
+ *)
+ atf_fail "wrong quota type"
+ ;;
+ esac
+
+ # create $nusers users, so we are sure the free list has entries
+ # from block 1. Start from 10, as non-root id is 1.
+ # set default to deny all
+ ( echo "@format netbsd-quota-dump v1"
+ echo "# idtype id objtype hard soft usage expire grace"
+ echo "$q default block 0 0 0 0 0"
+ echo "$q default file 0 0 0 0 0"
+ local i=10;
+ while [ $i -lt $(($nusers + 10)) ]; do
+ echo "$q $i block 0 0 0 0 0"
+ echo "$q $i file 0 0 0 0 0"
+ i=$((i + 1))
+ done
+ ) | atf_check -s exit:0 \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt quotarestore -d /mnt
+ atf_check -s exit:0 rump.halt
+ #now start the server which does the limits tests
+ $(atf_get_srcdir)/h_quota2_tests -oC -b 0 ${IMG} ${RUMP_SERVER}
+ rump_quota_shutdown
+}
diff --git a/contrib/netbsd-tests/fs/ffs/t_mount.c b/contrib/netbsd-tests/fs/ffs/t_mount.c
new file mode 100644
index 000000000000..20960953406b
--- /dev/null
+++ b/contrib/netbsd-tests/fs/ffs/t_mount.c
@@ -0,0 +1,138 @@
+/* $NetBSD: t_mount.c,v 1.14 2017/01/13 21:30:39 christos Exp $ */
+
+/*
+ * Basic tests for mounting
+ */
+
+/*
+ * 48Kimage:
+ * Adapted for rump and atf from a testcase supplied
+ * by Hubert Feyrer on netbsd-users@
+ */
+
+#include <atf-c.h>
+
+#define FSTEST_IMGSIZE (96 * 512)
+#include "../common/h_fsmacros.h"
+
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <stdlib.h>
+
+#include <ufs/ufs/ufsmount.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_macros.h"
+
+ATF_TC(48Kimage);
+ATF_TC_HEAD(48Kimage, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "mount small 48K ffs image");
+}
+
+ATF_TC_BODY(48Kimage, tc)
+{
+ void *tmp;
+
+ atf_tc_expect_fail("PR kern/43573");
+ FSTEST_CONSTRUCTOR(tc, ffs, tmp);
+ atf_tc_expect_pass();
+
+ FSTEST_DESTRUCTOR(tc, ffs, tmp);
+}
+
+ATF_TC(fsbsizeovermaxphys);
+ATF_TC_HEAD(fsbsizeovermaxphys, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "mounts file system with "
+ "blocksize > MAXPHYS");
+ /* PR kern/43727 */
+}
+
+ATF_TC_BODY(fsbsizeovermaxphys, tc)
+{
+ char cmd[1024];
+ struct ufs_args args;
+ struct statvfs svb;
+
+ /*
+ * We cannot pass newfs parameters via the fstest interface,
+ * so do things the oldfashioned manual way.
+ */
+ snprintf(cmd, sizeof(cmd), "newfs -G -b %d -F -s 10000 "
+ "ffs.img > /dev/null", MAXPHYS * 2);
+ if (system(cmd))
+ atf_tc_fail("cannot create file system");
+
+ rump_init();
+ if (rump_pub_etfs_register("/devdisk", "ffs.img", RUMP_ETFS_BLK))
+ atf_tc_fail("cannot register rump fake device");
+
+ args.fspec = __UNCONST("/devdisk");
+
+ if (rump_sys_mkdir("/mp", 0777) == -1)
+ atf_tc_fail_errno("create mountpoint");
+
+ /* mount succeeded? bad omen. confirm we're in trouble. */
+ if (rump_sys_mount(MOUNT_FFS, "/mp", 0, &args, sizeof(args)) != -1) {
+ rump_sys_statvfs1("/mp", &svb, ST_WAIT);
+ atf_tc_fail("not expecting to be alive");
+ }
+
+ /* otherwise we're do-ne */
+}
+
+ATF_TC(fsbsizeovermaxbsize);
+ATF_TC_HEAD(fsbsizeovermaxbsize, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "mounts file system with "
+ "blocksize > MAXBSIZE");
+}
+
+ATF_TC_BODY(fsbsizeovermaxbsize, tc)
+{
+ char cmd[1024];
+ struct ufs_args args;
+ struct statvfs svb;
+
+ /*
+ * We cannot pass newfs parameters via the fstest interface,
+ * so do things the oldfashioned manual way.
+ */
+ snprintf(cmd, sizeof(cmd), "newfs -G -b %d -F -s 10000 "
+ "ffs.img > /dev/null", MAXBSIZE * 2);
+ if (system(cmd))
+ atf_tc_fail("cannot create file system");
+
+ rump_init();
+ if (rump_pub_etfs_register("/devdisk", "ffs.img", RUMP_ETFS_BLK))
+ atf_tc_fail("cannot register rump fake device");
+
+ args.fspec = __UNCONST("/devdisk");
+
+ if (rump_sys_mkdir("/mp", 0777) == -1)
+ atf_tc_fail_errno("create mountpoint");
+
+ /* mount succeeded? bad omen. confirm we're in trouble. */
+ if (rump_sys_mount(MOUNT_FFS, "/mp", 0, &args, sizeof(args)) != -1) {
+ rump_sys_statvfs1("/mp", &svb, ST_WAIT);
+ atf_tc_fail("not expecting to be alive");
+ }
+
+ /* otherwise we're do-ne */
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, 48Kimage);
+ ATF_TP_ADD_TC(tp, fsbsizeovermaxphys);
+ ATF_TP_ADD_TC(tp, fsbsizeovermaxbsize);
+
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/ffs/t_quota2_1.c b/contrib/netbsd-tests/fs/ffs/t_quota2_1.c
new file mode 100644
index 000000000000..22cd5d24512a
--- /dev/null
+++ b/contrib/netbsd-tests/fs/ffs/t_quota2_1.c
@@ -0,0 +1,114 @@
+/* $NetBSD: t_quota2_1.c,v 1.5 2017/01/13 21:30:39 christos Exp $ */
+
+/*
+ * Basic tests for quota2
+ */
+
+#include <atf-c.h>
+
+#include "../common/h_fsmacros.h"
+
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <stdlib.h>
+
+#include <ufs/ufs/ufsmount.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_macros.h"
+
+static void
+do_quota(const atf_tc_t *tc, int n, const char *newfs_opts, int log)
+{
+ int i;
+ char buf[1024];
+ int res;
+ int fd;
+ struct ufs_args uargs;
+
+ snprintf(buf, sizeof(buf), "newfs -q user -q group -F -s 4000 -n %d "
+ "%s %s", (n + 3), newfs_opts, FSTEST_IMGNAME);
+ if (system(buf) == -1)
+ atf_tc_fail_errno("cannot create file system");
+
+ rump_init();
+ if (rump_sys_mkdir(FSTEST_MNTNAME, 0777) == -1)
+ atf_tc_fail_errno("mount point create");
+
+ rump_pub_etfs_register("/diskdev", FSTEST_IMGNAME, RUMP_ETFS_BLK);
+
+ uargs.fspec = __UNCONST("/diskdev");
+ if (rump_sys_mount(MOUNT_FFS, FSTEST_MNTNAME, (log) ? MNT_LOG : 0,
+ &uargs, sizeof(uargs)) == -1)
+ atf_tc_fail_errno("mount ffs %s", FSTEST_MNTNAME);
+
+ atf_tc_expect_pass();
+ FSTEST_ENTER();
+ RL(rump_sys_chown(".", 0, 0));
+ for (i = 0 ; i < n; i++) {
+ sprintf(buf, "file%d", i);
+ RL(fd = rump_sys_open(buf, O_CREAT | O_RDWR, 0755));
+ sprintf(buf, "test file no %d", i);
+ RL(rump_sys_write(fd, buf, strlen(buf)));
+ RL(rump_sys_fchown(fd, i, i+80000));
+ rump_sys_close(fd);
+ }
+ FSTEST_EXIT();
+ if (rump_sys_unmount(FSTEST_MNTNAME, 0) != 0) {
+ rump_pub_vfs_mount_print(FSTEST_MNTNAME, 1);
+ atf_tc_fail_errno("unmount failed");
+ }
+ snprintf(buf, 1024, "fsck_ffs -fn -F %s", FSTEST_IMGNAME);
+ res = system(buf);
+ if (res != 0)
+ atf_tc_fail("fsck returned %d", res);
+}
+
+#define DECL_TEST(nent, newops, name, descr, log) \
+ATF_TC(quota_##name); \
+ \
+ATF_TC_HEAD(quota_##name, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "test quotas with %d users and groups, %s", \
+ nent, descr); \
+} \
+ \
+ATF_TC_BODY(quota_##name, tc) \
+{ \
+ do_quota(tc, nent, newops, log); \
+}
+
+DECL_TEST(40, "-O1 -B le", 40_O1_le, "UFS1 little-endian", 0)
+DECL_TEST(40, "-O1 -B be", 40_O1_be, "UFS1 big-endian", 0)
+
+DECL_TEST(40, "-O2 -B le", 40_O2_le, "UFS2 little-endian", 0)
+DECL_TEST(40, "-O2 -B be", 40_O2_be, "UFS2 big-endian", 0)
+
+DECL_TEST(40, "-O1", 40_O1_log, "UFS1 log", 1)
+DECL_TEST(40, "-O2", 40_O2_log, "UFS2 log", 1)
+
+DECL_TEST(1000, "-O1 -B le", 1000_O1_le, "UFS1 little-endian", 0)
+DECL_TEST(1000, "-O1 -B be", 1000_O1_be, "UFS1 big-endian", 0)
+
+DECL_TEST(1000, "-O2 -B le", 1000_O2_le, "UFS2 little-endian", 0)
+DECL_TEST(1000, "-O2 -B be", 1000_O2_be, "UFS2 big-endian", 0)
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, quota_40_O1_le);
+ ATF_TP_ADD_TC(tp, quota_40_O1_be);
+ ATF_TP_ADD_TC(tp, quota_40_O2_le);
+ ATF_TP_ADD_TC(tp, quota_40_O2_be);
+ ATF_TP_ADD_TC(tp, quota_40_O1_log);
+ ATF_TP_ADD_TC(tp, quota_40_O2_log);
+ ATF_TP_ADD_TC(tp, quota_1000_O1_le);
+ ATF_TP_ADD_TC(tp, quota_1000_O1_be);
+ ATF_TP_ADD_TC(tp, quota_1000_O2_le);
+ ATF_TP_ADD_TC(tp, quota_1000_O2_be);
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/ffs/t_quota2_remount.c b/contrib/netbsd-tests/fs/ffs/t_quota2_remount.c
new file mode 100644
index 000000000000..d843cf891b4b
--- /dev/null
+++ b/contrib/netbsd-tests/fs/ffs/t_quota2_remount.c
@@ -0,0 +1,139 @@
+/* $NetBSD: t_quota2_remount.c,v 1.5 2017/01/13 21:30:39 christos Exp $ */
+
+/*
+ * Basic tests for quota2
+ */
+
+#include <atf-c.h>
+
+#include "../common/h_fsmacros.h"
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/statvfs.h>
+
+#include <stdlib.h>
+
+#include <ufs/ufs/ufsmount.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_macros.h"
+
+static void
+do_quota(const atf_tc_t *tc, int n, const char *newfs_opts, int log)
+{
+ int i;
+ char buf[1024];
+ int res;
+ int fd;
+ struct ufs_args uargs;
+ struct statvfs fst;
+
+ snprintf(buf, sizeof(buf), "newfs -q user -q group -F -s 4000 -n %d "
+ "%s %s", (n + 3), newfs_opts, FSTEST_IMGNAME);
+ if (system(buf) == -1)
+ atf_tc_fail_errno("cannot create file system");
+
+ rump_init();
+ if (rump_sys_mkdir(FSTEST_MNTNAME, 0777) == -1)
+ atf_tc_fail_errno("mount point create");
+
+ rump_pub_etfs_register("/diskdev", FSTEST_IMGNAME, RUMP_ETFS_BLK);
+
+ uargs.fspec = __UNCONST("/diskdev");
+
+ /* read-only doens't have quota enabled */
+ if (rump_sys_mount(MOUNT_FFS, FSTEST_MNTNAME, MNT_RDONLY,
+ &uargs, sizeof(uargs)) == -1)
+ atf_tc_fail_errno("mount ffs ro %s", FSTEST_MNTNAME);
+
+ if (rump_sys_statvfs1(FSTEST_MNTNAME, &fst, 0) != 0)
+ atf_tc_fail_errno("statbfs %s (1)", FSTEST_MNTNAME);
+
+ if ((fst.f_flag & ST_QUOTA) != 0)
+ atf_tc_fail("R/O filesystem has quota");
+
+ /* updating to read-write enables quota */
+ if (rump_sys_mount(MOUNT_FFS, FSTEST_MNTNAME,
+ MNT_UPDATE | (log ? MNT_LOG : 0), &uargs, sizeof(uargs)) == -1)
+ atf_tc_fail_errno("mount ffs rw %s", FSTEST_MNTNAME);
+
+ if (rump_sys_statvfs1(FSTEST_MNTNAME, &fst, 0) != 0)
+ atf_tc_fail_errno("statbfs %s (2)", FSTEST_MNTNAME);
+
+ if ((fst.f_flag & ST_QUOTA) == 0)
+ atf_tc_fail("R/W filesystem has no quota");
+
+ /* we can update a second time */
+ if (rump_sys_mount(MOUNT_FFS, FSTEST_MNTNAME,
+ MNT_UPDATE | (log ? MNT_LOG : 0), &uargs, sizeof(uargs)) == -1)
+ atf_tc_fail_errno("mount ffs rw(2) %s", FSTEST_MNTNAME);
+
+ if (rump_sys_statvfs1(FSTEST_MNTNAME, &fst, 0) != 0)
+ atf_tc_fail_errno("statbfs %s (3)", FSTEST_MNTNAME);
+
+ if ((fst.f_flag & ST_QUOTA) == 0)
+ atf_tc_fail("R/W filesystem has no quota");
+
+ /* create some files so fsck has something to check */
+ FSTEST_ENTER();
+ RL(rump_sys_chown(".", 0, 0));
+ for (i = 0 ; i < n; i++) {
+ sprintf(buf, "file%d", i);
+ RL(fd = rump_sys_open(buf, O_CREAT | O_RDWR, 0755));
+ sprintf(buf, "test file no %d", i);
+ RL(rump_sys_write(fd, buf, strlen(buf)));
+ RL(rump_sys_fchown(fd, i, i+80000));
+ rump_sys_close(fd);
+ }
+ FSTEST_EXIT();
+ if (rump_sys_unmount(FSTEST_MNTNAME, 0) != 0) {
+ rump_pub_vfs_mount_print(FSTEST_MNTNAME, 1);
+ atf_tc_fail_errno("unmount failed");
+ }
+ snprintf(buf, 1024, "fsck_ffs -fn -F %s", FSTEST_IMGNAME);
+ res = system(buf);
+ if (res != 0)
+ atf_tc_fail("fsck returned %d", res);
+}
+
+#define DECL_TEST(nent, newops, name, descr, log) \
+ATF_TC(quota_##name); \
+ \
+ATF_TC_HEAD(quota_##name, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "test filesystem remount with quotas, %s", descr); \
+} \
+ \
+ATF_TC_BODY(quota_##name, tc) \
+{ \
+ do_quota(tc, nent, newops, log); \
+}
+
+DECL_TEST(10, "-O1 -B le", 10_O1_le, "UFS1 little-endian", 0)
+DECL_TEST(10, "-O1 -B be", 10_O1_be, "UFS1 big-endian", 0)
+
+#if 0
+/*
+ * this cause fsck to complain about summaries at the end.
+ * This sems to be related to -o log (reproductible on a fs with no
+ * quota enabled). not reproductible with a real kernel ...
+ */
+DECL_TEST(10, "-O1", 10_O1_log, "UFS1 log", 1)
+DECL_TEST(10, "-O2", 10_O2_log, "UFS2 log", 1)
+#endif
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, quota_10_O1_le);
+ ATF_TP_ADD_TC(tp, quota_10_O1_be);
+#if 0
+ ATF_TP_ADD_TC(tp, quota_10_O1_log);
+ ATF_TP_ADD_TC(tp, quota_10_O2_log);
+#endif
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/ffs/t_quotalimit.sh b/contrib/netbsd-tests/fs/ffs/t_quotalimit.sh
new file mode 100755
index 000000000000..16e47b7a8fb8
--- /dev/null
+++ b/contrib/netbsd-tests/fs/ffs/t_quotalimit.sh
@@ -0,0 +1,345 @@
+# $NetBSD: t_quotalimit.sh,v 1.4 2012/01/18 20:51:23 bouyer Exp $
+#
+# Copyright (c) 2011 Manuel Bouyer
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+for e in le; do
+ for v in 1; do
+ for q in "user" "group"; do
+ test_case_root limit_${e}_${v}_${q} limit_quota \
+ "hit hard limit quota with ${q} enabled" -b ${e} ${v} ${q}
+ test_case_root limit_${e}_${v}_${q}_log limit_quota \
+ "hit hard limit quota with ${q} enabled, WAPBL" -bl ${e} ${v} ${q}
+ test_case_root slimit_${e}_${v}_${q} limit_softquota \
+ "hit soft limit quota with ${q} enabled after grace time" \
+ -b ${e} ${v} ${q}
+ test_case_root inolimit_${e}_${v}_${q} limit_iquota \
+ "hit hard limit ino quota with ${q} enabled" -b ${e} ${v} ${q}
+ test_case_root inolimit_${e}_${v}_${q}_log limit_iquota \
+ "hit hard limit ino quota with ${q} enabled, WAPBL" -bl ${e} ${v} ${q}
+ test_case_root sinolimit_${e}_${v}_${q} limit_softiquota \
+ "hit soft limit ino quota with ${q} enabled after grace time" \
+ -b ${e} ${v} ${q}
+ test_case_root herit_defq_${e}_${v}_${q} inherit_defaultquota \
+ "new id herit from default for ${q} quota" -b ${e} ${v} ${q}
+ test_case_root herit_defq_${e}_${v}_${q}_log inherit_defaultquota \
+ "new id herit from default for ${q} quota, WAPBL" -bl ${e} ${v} ${q}
+ test_case_root herit_idefq_${e}_${v}_${q}_log inherit_defaultiquota \
+ "new id herit from default for ${q} ino quota, WAPBL" -bl ${e} ${v} ${q}
+ done
+ done
+done
+
+limit_quota()
+{
+ create_ffs_server $*
+ local q=$4
+ local expect
+ local id=1
+
+ case ${q} in
+ user)
+ expect=u
+ fail=g
+ ;;
+ group)
+ expect=g
+ fail=u
+ ;;
+ both)
+ expect="u g"
+ fail=""
+ ;;
+ *)
+ atf_fail "wrong quota type"
+ ;;
+ esac
+
+ for q in ${expect} ; do
+ atf_check -s exit:0 \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt edquota -$q -s2k/4 -h3k/6 \
+ -t 2h/2h ${id}
+ done
+ atf_check -s exit:0 rump.halt
+
+ #now start the server which does the limits tests
+ atf_check -s exit:0 -o ignore \
+-e match:'test 0: write up to hard limit returned 69: Disc quota exceeded' \
+ $(atf_get_srcdir)/h_quota2_tests -b 0 ${IMG} ${RUMP_SERVER}
+ for q in ${expect} ; do
+ atf_check -s exit:0 \
+ -o match:'/mnt 3072 B\* 2048 B 3072 B 2:0 2 4 6 ' \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt quota -$q -h ${id}
+ atf_check -s exit:0 \
+ -o match:'daemon \+- 3 2 3 2:0 2 4 6' \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt repquota -$q /mnt
+ done
+ rump_quota_shutdown
+}
+
+limit_softquota()
+{
+ create_ffs_server $*
+ local q=$4
+ local expect
+ local id=1
+
+ case ${q} in
+ user)
+ expect=u
+ fail=g
+ ;;
+ group)
+ expect=g
+ fail=u
+ ;;
+ both)
+ expect="u g"
+ fail=""
+ ;;
+ *)
+ atf_fail "wrong quota type"
+ ;;
+ esac
+
+ for q in ${expect} ; do
+ atf_check -s exit:0 \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt edquota -$q -s2k/4 -h3k/6 \
+ -t 1s/1d ${id}
+ done
+ atf_check -s exit:0 rump.halt
+
+ #now start the server which does the limits tests
+ atf_check -s exit:0 -o ignore \
+-e match:'test 1: write beyond the soft limit after grace time returned 69: Disc quota exceeded' \
+ $(atf_get_srcdir)/h_quota2_tests -b 1 ${IMG} ${RUMP_SERVER}
+ for q in ${expect} ; do
+ atf_check -s exit:0 \
+ -o match:'/mnt 2560 B\* 2048 B 3072 B none 2 4 6 ' \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt quota -$q -h ${id}
+ atf_check -s exit:0 \
+ -o match:'daemon \+- 2 2 3 none 2 4 6' \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt repquota -$q /mnt
+ done
+ rump_quota_shutdown
+}
+
+limit_iquota()
+{
+ create_ffs_server $*
+ local q=$4
+ local expect
+ local id=1
+
+ case ${q} in
+ user)
+ expect=u
+ fail=g
+ ;;
+ group)
+ expect=g
+ fail=u
+ ;;
+ both)
+ expect="u g"
+ fail=""
+ ;;
+ *)
+ atf_fail "wrong quota type"
+ ;;
+ esac
+
+ for q in ${expect} ; do
+ atf_check -s exit:0 \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt edquota -$q -s2m/4 -h3m/6 \
+ -t 2h/2h ${id}
+ done
+ atf_check -s exit:0 rump.halt
+
+ #now start the server which does the limits tests
+ atf_check -s exit:0 -o ignore \
+-e match:'test 2: create file up to hard limit returned 69: Disc quota exceeded' \
+ $(atf_get_srcdir)/h_quota2_tests -b 2 ${IMG} ${RUMP_SERVER}
+ for q in ${expect} ; do
+ atf_check -s exit:0 \
+ -o match:'/mnt 3072 B 2048 K 3072 K 6 \* 4 6 2:0' \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt quota -$q -h ${id}
+ atf_check -s exit:0 \
+ -o match:'daemon -\+ 3 2048 3072 6 4 6 2:0' \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt repquota -$q /mnt
+ done
+ rump_quota_shutdown
+}
+
+limit_softiquota()
+{
+ create_ffs_server $*
+ local q=$4
+ local expect
+ local id=1
+
+ case ${q} in
+ user)
+ expect=u
+ fail=g
+ ;;
+ group)
+ expect=g
+ fail=u
+ ;;
+ both)
+ expect="u g"
+ fail=""
+ ;;
+ *)
+ atf_fail "wrong quota type"
+ ;;
+ esac
+
+ for q in ${expect} ; do
+ atf_check -s exit:0 \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt edquota -$q -s2m/4 -h3m/6 \
+ -t 1d/1s ${id}
+ done
+ atf_check -s exit:0 rump.halt
+
+ #now start the server which does the limits tests
+ atf_check -s exit:0 -o ignore \
+-e match:'test 3: create file beyond the soft limit after grace time returned 69: Disc quota exceeded' \
+ $(atf_get_srcdir)/h_quota2_tests -b 3 ${IMG} ${RUMP_SERVER}
+ for q in ${expect} ; do
+ atf_check -s exit:0 \
+ -o match:'/mnt 2560 B 2048 K 3072 K 5 \* 4 6 none' \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt quota -$q -h ${id}
+ atf_check -s exit:0 \
+ -o match:'daemon -\+ 2 2048 3072 5 4 6 none' \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt repquota -$q /mnt
+ done
+ rump_quota_shutdown
+}
+
+inherit_defaultquota()
+{
+ create_ffs_server $*
+ local q=$4
+ local expect
+ local id=1
+
+ case ${q} in
+ user)
+ expect=u
+ fail=g
+ ;;
+ group)
+ expect=g
+ fail=u
+ ;;
+ both)
+ expect="u g"
+ fail=""
+ ;;
+ *)
+ atf_fail "wrong quota type"
+ ;;
+ esac
+
+ for q in ${expect} ; do
+ atf_check -s exit:0 \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt edquota -$q -s2k/4 -h3k/6 \
+ -t 2h/2h -d
+ done
+ for q in ${expect} ; do
+ atf_check -s exit:0 \
+ -o match:'Disk quotas for .*id 1\): none' \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt quota -$q -v ${id}
+ done
+ atf_check -s exit:0 rump.halt
+
+ #now start the server which does the limits tests
+ atf_check -s exit:0 -o ignore \
+-e match:'test 0: write up to hard limit returned 69: Disc quota exceeded' \
+ $(atf_get_srcdir)/h_quota2_tests -b 0 ${IMG} ${RUMP_SERVER}
+ for q in ${expect} ; do
+ atf_check -s exit:0 \
+ -o match:'/mnt 3072 B\* 2048 B 3072 B 2:0 2 4 6 ' \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt quota -$q -h ${id}
+ atf_check -s exit:0 \
+ -o match:'daemon \+- 3 2 3 2:0 2 4 6' \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt repquota -$q /mnt
+ done
+ rump_quota_shutdown
+}
+
+inherit_defaultiquota()
+{
+ create_ffs_server $*
+ local q=$4
+ local expect
+ local id=1
+
+ case ${q} in
+ user)
+ expect=u
+ fail=g
+ ;;
+ group)
+ expect=g
+ fail=u
+ ;;
+ both)
+ expect="u g"
+ fail=""
+ ;;
+ *)
+ atf_fail "wrong quota type"
+ ;;
+ esac
+
+ for q in ${expect} ; do
+ atf_check -s exit:0 \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt edquota -$q -s2m/4 -h3m/6 \
+ -t 2h/2h -d
+ done
+ for q in ${expect} ; do
+ atf_check -s exit:0 \
+ -o match:'Disk quotas for .*id 1\): none' \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt quota -$q -v ${id}
+ done
+ atf_check -s exit:0 rump.halt
+
+ #now start the server which does the limits tests
+ atf_check -s exit:0 -o ignore \
+-e match:'test 2: create file up to hard limit returned 69: Disc quota exceeded' \
+ $(atf_get_srcdir)/h_quota2_tests -b 2 ${IMG} ${RUMP_SERVER}
+ for q in ${expect} ; do
+ atf_check -s exit:0 \
+ -o match:'/mnt 3072 B 2048 K 3072 K 6 \* 4 6 2:0' \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt quota -$q -h ${id}
+ atf_check -s exit:0 \
+ -o match:'daemon -\+ 3 2048 3072 6 4 6 2:0' \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt repquota -$q /mnt
+ done
+ rump_quota_shutdown
+}
diff --git a/contrib/netbsd-tests/fs/ffs/t_setquota.sh b/contrib/netbsd-tests/fs/ffs/t_setquota.sh
new file mode 100755
index 000000000000..5795fe4d46b6
--- /dev/null
+++ b/contrib/netbsd-tests/fs/ffs/t_setquota.sh
@@ -0,0 +1,203 @@
+# $NetBSD: t_setquota.sh,v 1.4 2012/01/18 20:51:23 bouyer Exp $
+#
+# Copyright (c) 2011 Manuel Bouyer
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+for e in le be; do
+ for v in 1 2; do
+ for q in "user" "group"; do
+ test_case_root set_${e}_${v}_${q} set_quota \
+ "set quota with ${q} enabled" -b ${e} ${v} ${q}
+ test_case_root set_new_${e}_${v}_${q} set_quota_new \
+ "set quota for new id with ${q} enabled" -b ${e} ${v} ${q}
+ test_case_root set_default_${e}_${v}_${q} set_quota_default \
+ "set default quota with ${q} enabled" -b ${e} ${v} ${q}
+ done
+ test_case_root set_${e}_${v}_"both" set_quota \
+ "set quota with both enabled" -b ${e} ${v} "both"
+ test_case_root set_new_${e}_${v}_"both" set_quota_new \
+ "set quota for new id with both enabled" -b ${e} ${v} "both"
+ test_case_root set_new_${e}_${v}_"both_log" set_quota_new \
+ "set quota for new id with both enabled, WAPBL" -bl ${e} ${v} "both"
+ test_case_root set_default_${e}_${v}_"both" set_quota_default \
+ "set default quota with both enabled" -b ${e} ${v} "both"
+ done
+done
+
+set_quota()
+{
+ create_ffs_server $*
+ local q=$4
+ local expect
+ local fail
+
+ case ${q} in
+ user)
+ expect=u
+ fail=g
+ ;;
+ group)
+ expect=g
+ fail=u
+ ;;
+ both)
+ expect="u g"
+ fail=""
+ ;;
+ *)
+ atf_fail "wrong quota type"
+ ;;
+ esac
+
+#check that we can set the expected quota
+ for q in ${expect} ; do
+ local id=$(id -${q})
+ atf_check -s exit:0 \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt edquota -$q -s10k/20 -h40M/50k \
+ -t 2W/3D ${id}
+ atf_check -s exit:0 \
+-o "match:/mnt 0 10 40960 2weeks 1 20 51200 3days" \
+-o "match:Disk quotas for .*: $" \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt quota -${q} -v
+ atf_check -s exit:0 \
+-o "match:-- 0 10 40960 1 20 51200" \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt repquota -${q} /mnt
+ done
+
+#check that we do not get positive reply for non-expected quota
+ for q in ${fail} ; do
+ local id=$(id -${q})
+ atf_check -s exit:0 \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt edquota -$q -s10k/20 -h40M/50k ${id}
+ atf_check -s exit:0 -o "not-match:/mnt" \
+ -o "not-match:Disk quotas for .*: $" \
+ -o "match:Disk quotas for .*: none$" \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt quota -${q} -v
+ atf_check -s exit:0 \
+-o "not-match:-- 0 - -" \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt repquota -${q} /mnt
+ done
+ rump_quota_shutdown
+}
+
+set_quota_new()
+{
+ create_ffs_server $*
+ local q=$4
+ local expect
+ local fail
+
+ case ${q} in
+ user)
+ expect=u
+ fail=g
+ ;;
+ group)
+ expect=g
+ fail=u
+ ;;
+ both)
+ expect="u g"
+ fail=""
+ ;;
+ *)
+ atf_fail "wrong quota type"
+ ;;
+ esac
+
+#check that we can set the expected quota
+ for q in ${expect} ; do
+ local id=1
+ atf_check -s exit:0 \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt edquota -$q -s10k/20 -h40M/50k \
+ -t 120W/255D ${id}
+ atf_check -s exit:0 \
+-o "match:/mnt 0 10 40960 2years 0 20 51200 9months" \
+-o "match:Disk quotas for .*: $" \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt quota -${q} -v ${id}
+ done
+
+#check that we do not get positive reply for non-expected quota
+ for q in ${fail} ; do
+ local id=$(id -${q})
+ atf_check -s exit:0 \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt edquota -$q -s10k/20 -h40M/50k ${id}
+ atf_check -s exit:0 -o "not-match:/mnt" \
+ -o "not-match:Disk quotas for .*: $" \
+ -o "match:Disk quotas for .*: none$" \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt quota -${q} -v ${id}
+ done
+ rump_quota_shutdown
+}
+
+set_quota_default()
+{
+ create_ffs_server $*
+ local q=$4
+ local expect
+ local fail
+
+ case ${q} in
+ user)
+ expect=u
+ fail=g
+ ;;
+ group)
+ expect=g
+ fail=u
+ ;;
+ both)
+ expect="u g"
+ fail=""
+ ;;
+ *)
+ atf_fail "wrong quota type"
+ ;;
+ esac
+
+#check that we can set the expected quota
+ for q in ${expect} ; do
+ local id="-d"
+ atf_check -s exit:0 \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt edquota -$q -s10k/20 -h40M/50k \
+ -t 2H2M/3540 ${id}
+ atf_check -s exit:0 \
+-o "match:/mnt 0 10 40960 2:2 0 20 51200 59" \
+-o "match:Default (user|group) disk quotas: $" \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt quota -${q} -v ${id}
+ done
+
+#check that we do not get positive reply for non-expected quota
+ for q in ${fail} ; do
+ local id="-d"
+ atf_check -s exit:0 \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt edquota -$q -s10k/20 -h40M/50k ${id}
+ atf_check -s exit:0 -o "not-match:/mnt" \
+ -o "not-match:Default (user|group) disk quotas: $" \
+ -o "match:Default (user|group) disk quotas: none$" \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/mnt quota -${q} -v ${id}
+ done
+ rump_quota_shutdown
+}
diff --git a/contrib/netbsd-tests/fs/ffs/t_snapshot.c b/contrib/netbsd-tests/fs/ffs/t_snapshot.c
new file mode 100644
index 000000000000..f661bec2634c
--- /dev/null
+++ b/contrib/netbsd-tests/fs/ffs/t_snapshot.c
@@ -0,0 +1,43 @@
+/* $NetBSD: t_snapshot.c,v 1.7 2017/01/13 21:30:39 christos Exp $ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include <ufs/ufs/ufsmount.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "h_macros.h"
+
+#define IMGNAME "ffs.img"
+#define NEWFS "newfs -F -s 10000 " IMGNAME
+#define FSCK "fsck_ffs -fn -F"
+#define BAKNAME "/mnt/le_snapp"
+
+static void
+mount_diskfs(const char *fspec, const char *path)
+{
+ struct ufs_args uargs;
+
+ uargs.fspec = __UNCONST(fspec);
+
+ if (rump_sys_mount(MOUNT_FFS, path, 0, &uargs, sizeof(uargs)) == -1)
+ atf_tc_fail_errno("mount ffs %s", path);
+}
+
+static void
+begin(void)
+{
+
+ /* empty */
+}
+
+#include "../common/snapshot.c"
diff --git a/contrib/netbsd-tests/fs/ffs/t_snapshot_log.c b/contrib/netbsd-tests/fs/ffs/t_snapshot_log.c
new file mode 100644
index 000000000000..31b28e53df2d
--- /dev/null
+++ b/contrib/netbsd-tests/fs/ffs/t_snapshot_log.c
@@ -0,0 +1,46 @@
+/* $NetBSD: t_snapshot_log.c,v 1.3 2017/01/13 21:30:39 christos Exp $ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include <ufs/ufs/ufsmount.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "h_macros.h"
+
+#define IMGNAME "ffs.img"
+#define NEWFS "newfs -F -s 10000 " IMGNAME
+#define FSCK "fsck_ffs -fn -F"
+#define BAKNAME "/mnt/le_snapp"
+
+static void
+mount_diskfs(const char *fspec, const char *path)
+{
+ struct ufs_args uargs;
+ static int flags = MNT_LOG;
+
+ uargs.fspec = __UNCONST(fspec);
+
+ if (rump_sys_mount(MOUNT_FFS,
+ path, flags, &uargs, sizeof(uargs)) == -1)
+ atf_tc_fail_errno("mount ffs %s", path);
+ flags = 0;
+}
+
+static void
+begin(void)
+{
+
+ /* empty */
+}
+
+#include "../common/snapshot.c"
diff --git a/contrib/netbsd-tests/fs/ffs/t_snapshot_v2.c b/contrib/netbsd-tests/fs/ffs/t_snapshot_v2.c
new file mode 100644
index 000000000000..b27929cb12d8
--- /dev/null
+++ b/contrib/netbsd-tests/fs/ffs/t_snapshot_v2.c
@@ -0,0 +1,43 @@
+/* $NetBSD: t_snapshot_v2.c,v 1.3 2017/01/13 21:30:39 christos Exp $ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include <ufs/ufs/ufsmount.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "h_macros.h"
+
+#define IMGNAME "ffs.img"
+#define NEWFS "newfs -F -s 10000 -O 2 " IMGNAME
+#define FSCK "fsck_ffs -fn -F"
+#define BAKNAME "/mnt/le_snapp"
+
+static void
+mount_diskfs(const char *fspec, const char *path)
+{
+ struct ufs_args uargs;
+
+ uargs.fspec = __UNCONST(fspec);
+
+ if (rump_sys_mount(MOUNT_FFS, path, 0, &uargs, sizeof(uargs)) == -1)
+ atf_tc_fail_errno("mount ffs %s", path);
+}
+
+static void
+begin(void)
+{
+
+ /* empty */
+}
+
+#include "../common/snapshot.c"
diff --git a/contrib/netbsd-tests/fs/fifofs/t_fifo.c b/contrib/netbsd-tests/fs/fifofs/t_fifo.c
new file mode 100644
index 000000000000..4b37bb85b8c2
--- /dev/null
+++ b/contrib/netbsd-tests/fs/fifofs/t_fifo.c
@@ -0,0 +1,238 @@
+/* Test case written by Bharat Joshi */
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_fifo.c,v 1.2 2017/01/10 22:36:29 christos Exp $");
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <err.h>
+#include <signal.h>
+
+#ifndef STANDALONE
+#include <atf-c.h>
+#endif
+
+#define FIFO_FILE_PATH "./fifo_file"
+#define NUM_MESSAGES 20
+#define MSG_SIZE 240
+#define MESSAGE "I am fine"
+
+static int verbose = 0;
+
+/*
+ * child_writer
+ *
+ * Function that runs in child context and opens and write to the FIFO.
+ */
+static void
+child_writer(void)
+{
+ ssize_t rv;
+ int fd;
+ size_t count;
+ char message[MSG_SIZE] = MESSAGE;
+ static const struct timespec ts = { 0, 10000 };
+
+ /* Open the fifo in write-mode */
+ for (;;) {
+ fd = open(FIFO_FILE_PATH, O_WRONLY, 0);
+ if (fd == -1) {
+ if (errno == EINTR)
+ continue;
+ err(1, "Child: can't open fifo in write mode");
+ }
+ break;
+ }
+
+ for (count = 0; count < NUM_MESSAGES; count++) {
+ rv = write(fd, message, MSG_SIZE);
+ if (rv == -1) {
+ warn("Child: Failed to write");
+ break;
+ }
+ if (rv != MSG_SIZE)
+ warnx("Child: wrote only %zd", rv);
+ nanosleep(&ts, NULL);
+ }
+
+ close(fd);
+ if (verbose) {
+ printf("Child: Closed the fifo file\n");
+ fflush(stdout);
+ }
+}
+
+/*
+ * _sigchild_handler
+ *
+ * Called when a sigchild is delivered
+ */
+static void
+sigchild_handler(int signo)
+{
+ if (verbose) {
+ if (signo == SIGCHLD) {
+ printf("Got sigchild\n");
+ } else {
+ printf("Got %d signal\n", signo);
+ }
+ fflush(stdout);
+ }
+
+}
+
+static int
+run(void)
+{
+ pid_t pid;
+ ssize_t rv;
+ int fd, status;
+ size_t buf_size = MSG_SIZE;
+ char buf[MSG_SIZE];
+ struct sigaction action;
+ static const struct timespec ts = { 0, 500000000 };
+
+ /* Catch sigchild Signal */
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = sigchild_handler;
+ sigemptyset(&action.sa_mask);
+
+ if (sigaction(SIGCHLD, &action, NULL) == -1)
+ err(1, "sigaction");
+
+ (void)unlink(FIFO_FILE_PATH);
+ /* First create a fifo */
+ if (mkfifo(FIFO_FILE_PATH, S_IRUSR | S_IWUSR) == -1)
+ err(1, "mkfifo");
+
+ switch ((pid = fork())) {
+ case -1:
+ err(1, "fork");
+ case 0:
+ /* Open the file in write mode so that subsequent read
+ * from parent side does not block the parent..
+ */
+ if ((fd = open(FIFO_FILE_PATH, O_WRONLY, 0)) == -1)
+ err(1, "failed to open fifo");
+
+ /* In child */
+ child_writer();
+ return 0;
+
+ default:
+ break;
+ }
+
+ if (verbose) {
+ printf("Child pid is %d\n", pid );
+ fflush(stdout);
+ }
+
+ /* In parent */
+ for (;;) {
+ if ((fd = open(FIFO_FILE_PATH, O_RDONLY, 0)) == -1) {
+ if (errno == EINTR)
+ continue;
+ else
+ err(1, "Failed to open the fifo in read mode");
+ }
+ /* Read mode is opened */
+ break;
+
+ }
+
+ nanosleep(&ts, NULL);
+ if (verbose) {
+ printf("Was sleeping...\n");
+ fflush(stdout);
+ }
+
+ for (;;) {
+ rv = read(fd, buf, buf_size);
+
+ if (rv == -1) {
+ warn("Failed to read");
+ if (errno == EINTR) {
+ if (verbose) {
+ printf("Parent interrupted, "
+ "continuing...\n");
+ fflush(stdout);
+ }
+ continue;
+ }
+
+ break;
+ }
+
+ if (rv == 0) {
+ if (verbose) {
+ printf("Writers have closed, looks like we "
+ "are done\n");
+ fflush(stdout);
+ }
+ break;
+ }
+
+ if (verbose) {
+ printf("Received %zd bytes message '%s'\n", rv, buf);
+ fflush(stdout);
+ }
+ }
+
+ close(fd);
+
+ if (verbose) {
+ printf("We are done.. now reap the child");
+ fflush(stdout);
+ }
+
+ // Read the child...
+ while (waitpid(pid, &status, 0) == -1)
+ if (errno != EINTR) {
+ warn("Failed to reap the child");
+ return 1;
+ }
+
+ if (verbose) {
+ printf("We are done completely\n");
+ fflush(stdout);
+ }
+ return 0;
+}
+
+#ifndef STANDALONE
+ATF_TC(parent_child);
+
+ATF_TC_HEAD(parent_child, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks that when a fifo is shared "
+ "between a reader parent and a writer child, that read will "
+ "return EOF, and not get stuck after the child exits");
+}
+
+ATF_TC_BODY(parent_child, tc)
+{
+ ATF_REQUIRE(run() == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, parent_child);
+
+ return atf_no_error();
+}
+#else
+int
+main(void)
+{
+ verbose = 1;
+ return run();
+}
+#endif
diff --git a/contrib/netbsd-tests/fs/h_funcs.subr b/contrib/netbsd-tests/fs/h_funcs.subr
new file mode 100644
index 000000000000..8da43cc95160
--- /dev/null
+++ b/contrib/netbsd-tests/fs/h_funcs.subr
@@ -0,0 +1,75 @@
+#!/bin/sh
+#
+# $NetBSD: h_funcs.subr,v 1.3 2010/06/23 11:19:17 pooka Exp $
+#
+# Copyright (c) 2007 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# require_fs name
+#
+# Checks that the given file system is built into the kernel and
+# that its corresponding mount(8) utility is available. Otherwise
+# skips the test.
+#
+require_fs() {
+ local name
+ local autoload
+ name="${1}"
+
+ atf_require_prog mount
+ atf_require_prog mount_${name}
+ atf_require_prog umount
+
+ # Begin FreeBSD
+ if true; then
+ if kldstat -qm ${name} || kldload -n ${name}; then
+ found=yes
+ else
+ found=no
+ fi
+ else
+ # End FreeBSD
+ # if we have autoloadable modules, just assume the file system
+ atf_require_prog sysctl
+ autoload=$(sysctl -n kern.module.autoload)
+ [ "${autoload}" = "1" ] && return 0
+
+ set -- $(sysctl -n vfs.generic.fstypes)
+ found=no
+ while [ ${#} -gt 1 ]; do
+ if [ ${1} = ${name} ]; then
+ found=yes
+ break
+ fi
+ shift
+ done
+ # Begin FreeBSD
+ fi
+ # End FreeBSD
+ [ ${found} = yes ] || \
+ atf_skip "The kernel does not include support the " \
+ "\`${name}' file system"
+}
diff --git a/contrib/netbsd-tests/fs/hfs/colon.hfs.bz2.uue b/contrib/netbsd-tests/fs/hfs/colon.hfs.bz2.uue
new file mode 100644
index 000000000000..9240e7befa80
--- /dev/null
+++ b/contrib/netbsd-tests/fs/hfs/colon.hfs.bz2.uue
@@ -0,0 +1,35 @@
+begin 644 colon.hfs.bz2
+M0EIH.3%!629363%?)5P``$+____[_UOQ>_]__^_O\+____1K30045,CBA$'P
+M'DA:2!KIT`3V\VRA*<ZPAUVK`E%-)E,IME3T33U--DFRCU,T33T@]-1HQ&1M
+M(/2&@&TGI&@9#1B#0&F@TT:``R9/4$I$4PTF$Q,B8T)IY(VD:9&`3(80T>B&
+M`$,"8C$P$T9!HR:81B,":`D20A3T:GI`9``#(>H-`:-```:`````````````
+M08!#1B:`R:83)ID`&$:-,`F`)A!H9`Q,@#``"8F3`3`30%241HC(4_4GJ>U-
+M1Y31IM)ID`]3(T>H`#0#0T`:!H!A&@!H`````*9A\>L.>@+P;@Y:4C2%YM29
+M)Z`H>8SU&'"0U30JHOQ^.'A/7!GQK%#<GT'!B0OU+G:^>A5RVH"2$4VDL&T@
+M65#T*A/GU+(_VI$+*$`TJ"4J1(B1S&-YV+\J^"\Z6EK+@TU[0OQJ+;*HW.MN
+M=HTL,CQHCE$C3#EW'>#8!IGYY[*)]U$!0W6L;#U:*4X6IT<@^*5!16E)%(:*
+MA["$B&JX1022R8T4#20D@+ZHA_2_9$72HDSJ-FJ"7Z15%U2`@<>BJGD5'+*A
+M@M6>?K^MS^LOC8I)EI6[`O3`#CCKU:M6F6A00-$/:LH+!E7(G33<)<I*5Q&T
+M4[$M.$5OJZQKO59^7&LNANB-1PEMLB*TT\<[=;@7C@EYFG#:K@GL&R8^48"Y
+MU*QN%C56,Q?,.BW;"R9.7;9Z;2,+07U,QGK13C,!FYO^3/?)>)OZ27I4`@;=
+M4C<-$,N=UX98G_0DK)BTS-^)R+8NZ,B3JU:@85&-MM@</$$"."U>FCNQW/?=
+M'ZL4BR[\&3S\>.K"S#1Z&>2I>^+16EM54PV3%K7M$"E`.N2"E*,8-@V.E<3$
+M,K\RU$&5E//5.'K6A+44]9=\"WF$J@Q1!43!WLA!?"XLR2GOU6G-,W]92)1`
+MC@M4A4'FVAA`Q&&_%1B@L_`Z;T`0>[W6SCI%W&=H%Y>2"#E>IN[R<S>3+(:S
+MLMC<>N$1&APB(&6_#U<"!5*#IDA;MLNX`J(:/B8I,UNWB3CDPU2.@"(ORCKK
+MK(1*"RZ+9V>$I2"PLER`U4BFIY'VTJ#\NYN2-9DRCIP1([QBLNKT:%1K"-P5
+MY)2NL9KM8=ACP>MLBS&J(00HA*QYRUL:'A%8F5]&22@@@D,;3[C,4.9$P)DK
+M>\N+DK'4TLDI*LD&>8DE<M`B3!)=Z,E/#BSM)O*_$9Z"ZPSI%3*/=47CG8DJ
+MD]U*[RTUH7SC41(FD1>V@DT9B\,_CR;`,Y-E8*MM?Z*?BPU`I[I5^G-B$$A.
+M\7&8DC*:*',@VKX0HL8O)DL<-9GG!8>NK.2XS2I.;Z3%Q9OIL8?(6-*DEGHW
+MX^&A?%APXI:C@,A/&<6=)7#$UI;1Y6A'C<#/EW-*T(8WC9=`B`%\)M5HIS[\
+MV1%+H,,()X19<,:)U:Y?V\-T!^]'A\%%%+Z6.PQ"QI$]3#11IRB<#.9-L)^!
+MB-WJYVQVMP^;7V<DK?-[M;DQF]<[:M3)$-F@*(CXT128KESC.#OPP+`$D`G-
+MA6$DKT2V\;3+*:U+2]66T]'1$A`=33.FMDA1G\MA$1V_"/+2]%!GTQI'8E03
+M=?AO,S0#0V,9IS,&;8KGKL_$RKJFE62392;]B2933`4\$VJ""-0$;")-/A]R
+M[D"*/0:F1=%1R!B9&8=^RD4B^;4O-6'*J4J)EVNWN[+!_$)1\;@+#>*W5MG'
+MKUWYE^[,*T:U)Y]NUI,00H%8>74G/?LJ0K)51^BXZ+AI*2O[PI`V*%Q@X\YV
+M!V?D08"^;*TYO4R)T6G5:M;_M5M*;H4V)1IKCL$01$`#1R%N5,'_B[DBG"A(
+%&*^2K@"^
+`
+end
diff --git a/contrib/netbsd-tests/fs/hfs/t_pathconvert.c b/contrib/netbsd-tests/fs/hfs/t_pathconvert.c
new file mode 100644
index 000000000000..056726a1a6c2
--- /dev/null
+++ b/contrib/netbsd-tests/fs/hfs/t_pathconvert.c
@@ -0,0 +1,83 @@
+/* $NetBSD: t_pathconvert.c,v 1.6 2017/01/13 21:30:40 christos Exp $ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <atf-c.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include <fs/hfs/hfs.h>
+
+#include "h_macros.h"
+
+ATF_TC(colonslash);
+ATF_TC_HEAD(colonslash, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "HFS+ colons/slashes (PR kern/44523)");
+ atf_tc_set_md_var(tc, "timeout", "20");
+}
+
+#define IMGNAME "colon.hfs"
+#define FAKEBLK "/dev/blk"
+#define FUNNY_FILENAME "foo:bar"
+ATF_TC_BODY(colonslash, tc)
+{
+ struct hfs_args args;
+ int dirfd, fd;
+ char thecmd[1024];
+ char buf[DIRBLKSIZ];
+ struct dirent *dirent;
+ int offset, nbytes;
+ bool ok = false;
+
+ snprintf(thecmd, sizeof(thecmd), "uudecode %s/colon.hfs.bz2.uue",
+ atf_tc_get_config_var(tc, "srcdir"));
+ RZ(system(thecmd));
+
+ snprintf(thecmd, sizeof(thecmd), "bunzip2 " IMGNAME ".bz2");
+ RZ(system(thecmd));
+
+ memset(&args, 0, sizeof args);
+ args.fspec = __UNCONST(FAKEBLK);
+ RZ(rump_init());
+
+ RL(rump_sys_mkdir("/mp", 0777));
+ RZ(rump_pub_etfs_register(FAKEBLK, IMGNAME, RUMP_ETFS_BLK));
+ RL(rump_sys_mount(MOUNT_HFS, "/mp", 0, &args, sizeof args));
+
+ RL(dirfd = rump_sys_open("/mp", O_RDONLY));
+
+ RL(nbytes = rump_sys_getdents(dirfd, buf, sizeof buf));
+
+ for (offset = 0; offset < nbytes; offset += dirent->d_reclen) {
+ dirent = (struct dirent *)(buf + offset);
+ if (strchr(dirent->d_name, '/'))
+ atf_tc_fail("dirent with slash: %s", dirent->d_name);
+ if (0 == strcmp(FUNNY_FILENAME, dirent->d_name))
+ ok = true;
+ }
+
+ if (!ok)
+ atf_tc_fail("no dirent for file: %s", FUNNY_FILENAME);
+
+ RL(rump_sys_close(dirfd));
+ RL(fd = rump_sys_open("/mp/" FUNNY_FILENAME, O_RDONLY));
+ RL(rump_sys_close(fd));
+ RL(rump_sys_unmount("/mp", 0));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, colonslash);
+ return 0;
+}
diff --git a/contrib/netbsd-tests/fs/kernfs/t_basic.c b/contrib/netbsd-tests/fs/kernfs/t_basic.c
new file mode 100644
index 000000000000..77acd1149c66
--- /dev/null
+++ b/contrib/netbsd-tests/fs/kernfs/t_basic.c
@@ -0,0 +1,133 @@
+/* $NetBSD: t_basic.c,v 1.4 2017/01/13 21:30:40 christos Exp $ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/module.h>
+#include <sys/dirent.h>
+#include <sys/sysctl.h>
+
+#include <atf-c.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include <miscfs/kernfs/kernfs.h>
+
+#include "h_macros.h"
+
+ATF_TC(getdents);
+ATF_TC_HEAD(getdents, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "kernfs directory contains files");
+}
+
+static void
+mountkernfs(void)
+{
+
+ rump_init();
+
+ if (rump_sys_mkdir("/kern", 0777) == -1)
+ atf_tc_fail_errno("mkdir /kern");
+ if (rump_sys_mount(MOUNT_KERNFS, "/kern", 0, NULL, 0) == -1)
+ atf_tc_fail_errno("could not mount kernfs");
+}
+
+ATF_TC_BODY(getdents, tc)
+{
+ struct dirent *dent;
+ char buf[8192];
+ int dfd;
+
+ mountkernfs();
+
+ if ((dfd = rump_sys_open("/kern", O_RDONLY)) == -1)
+ atf_tc_fail_errno("can't open directory");
+ if (rump_sys_getdents(dfd, buf, sizeof(buf)) == -1)
+ atf_tc_fail_errno("getdents");
+
+ /*
+ * Check that we get the first three values (., .., boottime).
+ * Make more complete by autogenerating list from kernfs_vnops.c?
+ */
+ dent = (void *)buf;
+ ATF_REQUIRE_STREQ(dent->d_name, ".");
+ dent = _DIRENT_NEXT(dent);
+ ATF_REQUIRE_STREQ(dent->d_name, "..");
+ dent = _DIRENT_NEXT(dent);
+ ATF_REQUIRE_STREQ(dent->d_name, "boottime");
+
+ /* done */
+}
+
+ATF_TC(hostname);
+ATF_TC_HEAD(hostname, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "/kern/hostname changes hostname");
+}
+
+static char *
+getthehost(void)
+{
+ static char buf[8192];
+ int mib[2];
+ size_t blen;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_HOSTNAME;
+ blen = sizeof(buf);
+ if (rump_sys___sysctl(mib, 2, buf, &blen, NULL, 0) == -1)
+ atf_tc_fail_errno("sysctl gethostname");
+
+ return buf;
+}
+
+#define NEWHOSTNAME "turboton roos-berg"
+ATF_TC_BODY(hostname, tc)
+{
+ char buf[8192];
+ char *shost, *p;
+ int fd;
+
+ mountkernfs();
+ if ((fd = rump_sys_open("/kern/hostname", O_RDWR)) == -1)
+ atf_tc_fail_errno("open hostname");
+
+ /* check initial match */
+ shost = getthehost();
+ buf[0] = '\0';
+ if (rump_sys_read(fd, buf, sizeof(buf)) == -1)
+ atf_tc_fail_errno("read hostname");
+ p = strchr(buf, '\n');
+ if (p)
+ *p = '\0';
+ ATF_REQUIRE_STREQ_MSG(buf, shost, "initial hostname mismatch");
+
+ /* check changing hostname works */
+ if (rump_sys_pwrite(fd, NEWHOSTNAME, strlen(NEWHOSTNAME), 0)
+ != strlen(NEWHOSTNAME)) {
+ atf_tc_fail_errno("write new hostname");
+ }
+
+ shost = getthehost();
+ ATF_REQUIRE_STREQ_MSG(NEWHOSTNAME, shost, "modified hostname mismatch");
+
+ /* done */
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, hostname);
+ ATF_TP_ADD_TC(tp, getdents);
+
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/lfs/t_pr.c b/contrib/netbsd-tests/fs/lfs/t_pr.c
new file mode 100644
index 000000000000..ed676d8bf9b0
--- /dev/null
+++ b/contrib/netbsd-tests/fs/lfs/t_pr.c
@@ -0,0 +1,60 @@
+/* $NetBSD: t_pr.c,v 1.7 2017/01/13 21:30:40 christos Exp $ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include <ufs/ufs/ufsmount.h>
+
+#include "h_macros.h"
+
+ATF_TC(mknod);
+ATF_TC_HEAD(mknod, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "mknod(2) hangs on LFS (PR kern/43503)");
+ atf_tc_set_md_var(tc, "timeout", "20");
+}
+
+#define IMGNAME "disk.img"
+#define FAKEBLK "/dev/blk"
+ATF_TC_BODY(mknod, tc)
+{
+ struct ufs_args args;
+
+ /* hmm, maybe i should fix newfs_lfs instead? */
+ if (system("newfs_lfs -D -F -s 10000 ./" IMGNAME) == -1)
+ atf_tc_fail_errno("newfs failed");
+
+ memset(&args, 0, sizeof(args));
+ args.fspec = __UNCONST(FAKEBLK);
+
+ rump_init();
+ if (rump_sys_mkdir("/mp", 0777) == -1)
+ atf_tc_fail_errno("cannot create mountpoint");
+ rump_pub_etfs_register(FAKEBLK, IMGNAME, RUMP_ETFS_BLK);
+ if (rump_sys_mount(MOUNT_LFS, "/mp", 0, &args, sizeof(args)) == -1)
+ atf_tc_fail_errno("rump_sys_mount failed");
+
+ //atf_tc_expect_timeout("PR kern/43503");
+ if (rump_sys_mknod("/mp/node", S_IFCHR | 0777, 0) == -1)
+ atf_tc_fail_errno("mknod failed");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, mknod);
+ return 0;
+}
diff --git a/contrib/netbsd-tests/fs/msdosfs/t_snapshot.c b/contrib/netbsd-tests/fs/msdosfs/t_snapshot.c
new file mode 100644
index 000000000000..449e62545ab7
--- /dev/null
+++ b/contrib/netbsd-tests/fs/msdosfs/t_snapshot.c
@@ -0,0 +1,51 @@
+/* $NetBSD: t_snapshot.c,v 1.4 2017/01/13 21:30:40 christos Exp $ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include <fs/tmpfs/tmpfs_args.h>
+#include <msdosfs/msdosfsmount.h>
+
+#include <atf-c.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "h_macros.h"
+
+#define IMGNAME "msdosfs.img"
+#define NEWFS "newfs_msdos -C 5M " IMGNAME
+#define FSCK "fsck_msdos -fn"
+#define BAKNAME "/stor/snap"
+
+static void
+mount_diskfs(const char *fspec, const char *path)
+{
+ struct msdosfs_args margs;
+
+ memset(&margs, 0, sizeof(margs));
+ margs.fspec = __UNCONST(fspec);
+ margs.version = MSDOSFSMNT_VERSION;
+
+ if (rump_sys_mount(MOUNT_MSDOS, path, 0, &margs, sizeof(margs)) == -1)
+ err(1, "mount msdosfs %s", path);
+}
+
+static void
+begin(void)
+{
+ struct tmpfs_args targs = { .ta_version = TMPFS_ARGS_VERSION, };
+
+ if (rump_sys_mkdir("/stor", 0777) == -1)
+ atf_tc_fail_errno("mkdir /stor");
+ if (rump_sys_mount(MOUNT_TMPFS, "/stor", 0, &targs,sizeof(targs)) == -1)
+ atf_tc_fail_errno("mount storage");
+}
+
+#include "../common/snapshot.c"
diff --git a/contrib/netbsd-tests/fs/nfs/nfsservice/README b/contrib/netbsd-tests/fs/nfs/nfsservice/README
new file mode 100644
index 000000000000..8cfa8af62949
--- /dev/null
+++ b/contrib/netbsd-tests/fs/nfs/nfsservice/README
@@ -0,0 +1,16 @@
+ $NetBSD: README,v 1.1 2010/07/26 15:53:00 pooka Exp $
+
+This directory contains the necessary bits to get an NFS server
+running in a rump kernel. In essence, it's:
+
+ * rpcbind
+ * mountd
+ * nfsd
+
+Additionally, you need the libc rpc code which is in
+tests/fs/common/nfsrpc.
+
+TODO: make the standard nfs userspace services usable (the challenge
+comes from rpc being in libc).
+
+questions? ==> pooka@netbsd.org
diff --git a/contrib/netbsd-tests/fs/nfs/nfsservice/exports b/contrib/netbsd-tests/fs/nfs/nfsservice/exports
new file mode 100644
index 000000000000..6cc8c5fb80e9
--- /dev/null
+++ b/contrib/netbsd-tests/fs/nfs/nfsservice/exports
@@ -0,0 +1,12 @@
+# $NetBSD: exports,v 1.2 2010/12/31 18:11:27 pooka Exp $
+#
+
+#
+# The export dir is currently hardcoded and is exposed to the
+# world, where "world" in this case means inside the rump shmif
+# IP network, i.e. not a very big world. Probably needs some
+# adjustments if we want to test NFS features more carefully,
+# but this is enough for the current VFS level tests.
+#
+/myexport -noresvport -noresvmnt -maproot=0:0 10.3.2.2
+/myexport -ro -noresvport -noresvmnt -maproot=0:0 10.4.2.2
diff --git a/contrib/netbsd-tests/fs/nfs/nfsservice/getmntinfo.c b/contrib/netbsd-tests/fs/nfs/nfsservice/getmntinfo.c
new file mode 100644
index 000000000000..0c0587a7aba5
--- /dev/null
+++ b/contrib/netbsd-tests/fs/nfs/nfsservice/getmntinfo.c
@@ -0,0 +1,85 @@
+/* $NetBSD: getmntinfo.c,v 1.1 2010/07/26 15:53:00 pooka Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)getmntinfo.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: getmntinfo.c,v 1.1 2010/07/26 15:53:00 pooka Exp $");
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#define getvfsstat(a,b,c) rump_sys_getvfsstat(a,b,c)
+
+/*
+ * Return information about mounted filesystems.
+ */
+int
+getmntinfo(mntbufp, flags)
+ struct statvfs **mntbufp;
+ int flags;
+{
+ static struct statvfs *mntbuf;
+ static int mntsize;
+ static size_t bufsize;
+
+ _DIAGASSERT(mntbufp != NULL);
+
+ if (mntsize <= 0 &&
+ (mntsize = getvfsstat(NULL, (size_t)0, MNT_NOWAIT)) == -1)
+ return (0);
+ if (bufsize > 0 &&
+ (mntsize = getvfsstat(mntbuf, bufsize, flags)) == -1)
+ return (0);
+ while (bufsize <= mntsize * sizeof(struct statvfs)) {
+ if (mntbuf)
+ free(mntbuf);
+ bufsize = (mntsize + 1) * sizeof(struct statvfs);
+ if ((mntbuf = malloc(bufsize)) == NULL)
+ return (0);
+ if ((mntsize = getvfsstat(mntbuf, bufsize, flags)) == -1)
+ return (0);
+ }
+ *mntbufp = mntbuf;
+ return (mntsize);
+}
diff --git a/contrib/netbsd-tests/fs/nfs/nfsservice/pathnames.h b/contrib/netbsd-tests/fs/nfs/nfsservice/pathnames.h
new file mode 100644
index 000000000000..3985af93850b
--- /dev/null
+++ b/contrib/netbsd-tests/fs/nfs/nfsservice/pathnames.h
@@ -0,0 +1,37 @@
+/* $NetBSD: pathnames.h,v 1.1 2010/07/26 15:53:00 pooka Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/5/93
+ */
+
+#include <paths.h>
+
+#define _PATH_EXPORTS "/etc/exports"
+#define _PATH_RMOUNTLIST "/var/db/mountdtab"
diff --git a/contrib/netbsd-tests/fs/nfs/nfsservice/rumpnfsd.c b/contrib/netbsd-tests/fs/nfs/nfsservice/rumpnfsd.c
new file mode 100644
index 000000000000..5f8e15250532
--- /dev/null
+++ b/contrib/netbsd-tests/fs/nfs/nfsservice/rumpnfsd.c
@@ -0,0 +1,166 @@
+/* $NetBSD: rumpnfsd.c,v 1.9 2015/11/08 02:45:16 christos Exp $ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <dlfcn.h>
+#include <err.h>
+#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <rpc/rpc.h>
+
+void *mountd_main(void *);
+void *rpcbind_main(void *);
+int nfsd_main(int, char **);
+
+sem_t gensem;
+
+#include "../../../net/config/netconfig.c"
+#include "../../common/h_fsmacros.h"
+#include "svc_fdset.h"
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+int
+main(int argc, char *argv[])
+{
+ const char *ethername, *ethername_ro;
+ const char *serveraddr, *serveraddr_ro;
+ const char *netmask;
+ const char *exportpath;
+ const char *imagename;
+ char ifname[IFNAMSIZ], ifname_ro[IFNAMSIZ];
+ void *fsarg;
+ pthread_t t;
+ int rv;
+
+ /* for netcfg */
+ noatf = 1;
+
+ /* use defaults? */
+ if (argc == 1) {
+ ethername = "etherbus";
+ ethername_ro = "etherbus_ro";
+ serveraddr = "10.3.2.1";
+ serveraddr_ro = "10.4.2.1";
+ netmask = "255.255.255.0";
+ exportpath = "/myexport";
+ imagename = "ffs.img";
+ } else {
+ ethername = argv[1];
+ ethername_ro = argv[2];
+ serveraddr = argv[3];
+ serveraddr_ro = argv[4];
+ netmask = argv[5];
+ exportpath = argv[6];
+ imagename = argv[7];
+ }
+
+ rump_init();
+ svc_fdset_init(SVC_FDSET_MT);
+
+ rv = rump_pub_etfs_register("/etc/exports", "./exports", RUMP_ETFS_REG);
+ if (rv) {
+ errx(1, "register /etc/exports: %s", strerror(rv));
+ }
+
+ /* mini-mtree for mountd */
+ static const char *const dirs[] = { "/var", "/var/run", "/var/db" };
+ for (size_t i = 0; i < __arraycount(dirs); i++)
+ if (rump_sys_mkdir(dirs[i], 0777) == -1)
+ err(1, "can't mkdir `%s'", dirs[i]);
+
+ if (ffs_fstest_newfs(NULL, &fsarg,
+ imagename, FSTEST_IMGSIZE, NULL) != 0)
+ err(1, "newfs failed");
+ if (ffs_fstest_mount(NULL, fsarg, exportpath, 0) != 0)
+ err(1, "mount failed");
+
+#if 0
+ /*
+ * Serve from host instead of dedicated mount?
+ * THIS IS MORE EVIL THAN MURRAY THE DEMONIC TALKING SKULL!
+ */
+
+ if (ukfs_modload("/usr/lib/librumpfs_syspuffs.so") < 1)
+ errx(1, "modload");
+
+ mount_syspuffs_parseargs(__arraycount(pnullarg), pnullarg,
+ &args, &mntflags, canon_dev, canon_dir);
+ if ((ukfs = ukfs_mount(MOUNT_PUFFS, "/", UKFS_DEFAULTMP, MNT_RDONLY,
+ &args, sizeof(args))) == NULL)
+ err(1, "mount");
+
+ if (ukfs_modload("/usr/lib/librumpfs_nfsserver.so") < 1)
+ errx(1, "modload");
+#endif
+
+ if (sem_init(&gensem, 1, 0) == -1)
+ err(1, "gensem init");
+
+ /* create interface */
+ netcfg_rump_makeshmif(ethername, ifname);
+ netcfg_rump_if(ifname, serveraddr, netmask);
+
+ netcfg_rump_makeshmif(ethername_ro, ifname_ro);
+ netcfg_rump_if(ifname_ro, serveraddr_ro, netmask);
+
+ /*
+ * No syslogging, thanks.
+ * XXX: "0" does not modify the mask, so pick something
+ * which is unlikely to cause any logging
+ */
+ setlogmask(0x10000000);
+
+ if (pthread_create(&t, NULL, rpcbind_main, NULL) == -1)
+ err(1, "rpcbind");
+ sem_wait(&gensem);
+
+ if (pthread_create(&t, NULL, mountd_main, NULL) == -1)
+ err(1, "mountd");
+ sem_wait(&gensem);
+
+ rv = 0;
+ /* signal the other process we're almost done */
+ if (write(3, &rv, 4) != 4)
+ errx(1, "magic write failed");
+
+ {
+ char *nfsargv[] = { __UNCONST("nfsd"), NULL };
+ nfsd_main(1, nfsargv);
+ }
+ /*NOTREACHED*/
+
+ return 0;
+}
diff --git a/contrib/netbsd-tests/fs/nfs/t_mountd.c b/contrib/netbsd-tests/fs/nfs/t_mountd.c
new file mode 100644
index 000000000000..6272587d71a0
--- /dev/null
+++ b/contrib/netbsd-tests/fs/nfs/t_mountd.c
@@ -0,0 +1,121 @@
+/* $NetBSD: t_mountd.c,v 1.6 2017/01/13 21:30:40 christos Exp $ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_macros.h"
+#include "../common/h_fsmacros.h"
+
+ATF_TC(mountdhup);
+ATF_TC_HEAD(mountdhup, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "test for service interrupt while "
+ "mountd handles SIGHUP");
+}
+
+static volatile int quit;
+
+static void *
+wrkwrkwrk(void *unused)
+{
+ int fd, fail;
+
+ fail = 0;
+
+ rump_sys_chdir(FSTEST_MNTNAME);
+ while (!quit) {
+ fd = rump_sys_open("file", O_RDWR | O_CREAT);
+ if (fd == -1) {
+ if (errno == EACCES) {
+ fail++;
+ break;
+ }
+ }
+ rump_sys_close(fd);
+ if (rump_sys_unlink("file") == -1) {
+ if (errno == EACCES) {
+ fail++;
+ break;
+ }
+ }
+ }
+ rump_sys_chdir("/");
+ quit = 1;
+
+ return fail ? wrkwrkwrk : NULL;
+}
+
+ATF_TC_BODY(mountdhup, tc)
+{
+ pthread_t pt;
+ struct nfstestargs *nfsargs;
+ void *voidargs;
+ int attempts;
+ void *fail;
+
+ FSTEST_CONSTRUCTOR(tc, nfs, voidargs);
+ nfsargs = voidargs;
+
+ pthread_create(&pt, NULL, wrkwrkwrk, NULL);
+ for (attempts = 100; attempts && !quit; attempts--) {
+ usleep(100000);
+ kill(nfsargs->ta_childpid, SIGHUP);
+ }
+ quit = 1;
+ pthread_join(pt, &fail);
+
+ FSTEST_DESTRUCTOR(tc, nfs, voidargs);
+
+ atf_tc_expect_fail("PR kern/5844");
+ if (fail)
+ atf_tc_fail("op failed with EACCES");
+ else
+ atf_tc_fail("race did not trigger this time");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, mountdhup);
+
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/nfs/t_rquotad.sh b/contrib/netbsd-tests/fs/nfs/t_rquotad.sh
new file mode 100755
index 000000000000..dc9226ce17cc
--- /dev/null
+++ b/contrib/netbsd-tests/fs/nfs/t_rquotad.sh
@@ -0,0 +1,142 @@
+# $NetBSD: t_rquotad.sh,v 1.5 2016/08/10 23:25:39 kre Exp $
+#
+# Copyright (c) 2011 Manuel Bouyer
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+for e in le be; do
+ for v in 1; do
+ for q in "user" "group" "both"; do
+ test_case_root get_nfs_${e}_${v}_${q} get_nfs_quota \
+ "get NFS quota with ${q} enabled" ${e} ${v} ${q}
+ done
+ done
+done
+
+get_nfs_quota()
+{
+ create_ffs $*
+ local q=$3
+ local expect
+
+ case ${q} in
+ user)
+ expect=u
+ ;;
+ group)
+ expect=g
+ ;;
+ both)
+ expect="u g"
+ ;;
+ *)
+ atf_fail "wrong quota type"
+ ;;
+ esac
+
+#start a a nfs server
+
+ atf_check -s exit:0 rump_server -lrumpvfs -lrumpdev -lrumpnet \
+ -lrumpnet_net -lrumpnet_netinet -lrumpnet_netinet6 \
+ -lrumpnet_local -lrumpnet_shmif -lrumpdev_disk -lrumpfs_ffs \
+ -lrumpfs_nfs -lrumpfs_nfsserver \
+ -d key=/dk,hostpath=${IMG},size=host ${RUMP_SERVER}
+
+ atf_check -s exit:0 rump.ifconfig shmif0 create
+ atf_check -s exit:0 rump.ifconfig shmif0 linkstr shmbus
+ atf_check -s exit:0 rump.ifconfig shmif0 inet 10.1.1.1
+
+ export RUMPHIJACK_RETRYCONNECT=die
+ export LD_PRELOAD=/usr/lib/librumphijack.so
+
+ atf_check -s exit:0 mkdir /rump/etc
+ atf_check -s exit:0 mkdir /rump/export
+ atf_check -s exit:0 mkdir -p /rump/var/run
+ atf_check -s exit:0 mkdir -p /rump/var/db
+ atf_check -s exit:0 touch /rump/var/db/mountdtab
+
+ /bin/echo "/export -noresvport -noresvmnt 10.1.1.100" | \
+ dd of=/rump/etc/exports 2> /dev/null
+
+ atf_check -s exit:0 -e ignore mount_ffs /dk /rump/export
+
+#set a quota limit (and check that we can read it back)
+ for q in ${expect} ; do
+ local id=$(id -${q})
+ atf_check -s exit:0 \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/export edquota -$q -s10k/20 -h40M/50k \
+ -t 2W/3D ${id}
+ atf_check -s exit:0 \
+-o "match:0 10 40960 2weeks 1 20 51200 3days" \
+-o "match:Disk quotas for .*: $" \
+ env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=vfs=getvfsstat,blanket=/export quota -${q} -v
+ done
+
+ # start rpcbind. we want /var/run/rpcbind.sock
+ export RUMPHIJACK='blanket=/var/run,socket=all'
+ atf_check -s exit:0 rpcbind
+
+ # ok, then we want mountd in the similar fashion
+ export RUMPHIJACK='blanket=/var/run:/var/db:/export,socket=all,path=/rump,vfs=all'
+ atf_check -s exit:0 mountd /rump/etc/exports
+
+ # and nfs
+ export RUMPHIJACK='blanket=/var/run,socket=all,vfs=all'
+ atf_check -s exit:0 nfsd
+
+ #finally, rpc.rquotad
+ export RUMPHIJACK='blanket=/var/run:/export,vfs=getvfsstat,socket=all'
+ atf_check -s exit:0 /usr/libexec/rpc.rquotad
+
+ # now start a client server
+ export RUMP_SERVER=unix://clientsock
+ RUMP_SOCKETS_LIST="${RUMP_SOCKETS_LIST} clientsock"
+ unset RUMPHIJACK
+ unset LD_PRELOAD
+
+ atf_check -s exit:0 rump_server -lrumpvfs -lrumpnet -lrumpdev \
+ -lrumpnet_net -lrumpnet_netinet -lrumpnet_shmif -lrumpfs_nfs\
+ ${RUMP_SERVER}
+
+ atf_check -s exit:0 rump.ifconfig shmif0 create
+ atf_check -s exit:0 rump.ifconfig shmif0 linkstr shmbus
+ atf_check -s exit:0 rump.ifconfig shmif0 inet 10.1.1.100
+
+ export LD_PRELOAD=/usr/lib/librumphijack.so
+
+ atf_check -s exit:0 mkdir /rump/mnt
+ atf_check -s exit:0 mount_nfs 10.1.1.1:/export /rump/mnt
+
+ #now try a quota(8) call
+ export RUMPHIJACK='blanket=/mnt,socket=all,path=/rump,vfs=getvfsstat'
+ for q in ${expect} ; do
+ local id=$(id -${q})
+ atf_check -s exit:0 \
+-o "match:/mnt 0 10 40960 1 20 51200 " \
+-o "match:Disk quotas for .*: $" \
+ quota -${q} -v
+ done
+
+ unset LD_PRELOAD
+ rump_quota_shutdown
+}
diff --git a/contrib/netbsd-tests/fs/nullfs/t_basic.c b/contrib/netbsd-tests/fs/nullfs/t_basic.c
new file mode 100644
index 000000000000..d54f7739bafc
--- /dev/null
+++ b/contrib/netbsd-tests/fs/nullfs/t_basic.c
@@ -0,0 +1,174 @@
+/* $NetBSD: t_basic.c,v 1.4 2017/01/13 21:30:40 christos Exp $ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <atf-c.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include <miscfs/nullfs/null.h>
+#include <fs/tmpfs/tmpfs_args.h>
+
+#include "h_macros.h"
+
+ATF_TC(basic);
+ATF_TC_HEAD(basic, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "basic nullfs functionality");
+}
+
+#define MSTR "magic bus"
+
+static void
+xput_tfile(const char *path, const char *mstr)
+{
+ int fd;
+
+ fd = rump_sys_open(path, O_CREAT | O_RDWR, 0777);
+ if (fd == -1)
+ atf_tc_fail_errno("create %s", path);
+ if (rump_sys_write(fd, MSTR, sizeof(MSTR)) != sizeof(MSTR))
+ atf_tc_fail_errno("write to testfile");
+ rump_sys_close(fd);
+}
+
+static int
+xread_tfile(const char *path, const char *mstr)
+{
+ char buf[128];
+ int fd;
+
+ fd = rump_sys_open(path, O_RDONLY);
+ if (fd == -1)
+ return errno;
+ if (rump_sys_read(fd, buf, sizeof(buf)) == -1)
+ atf_tc_fail_errno("read tfile");
+ rump_sys_close(fd);
+ if (strcmp(buf, MSTR) == 0)
+ return 0;
+ return EPROGMISMATCH;
+}
+
+static void
+mountnull(const char *what, const char *mp, int flags)
+{
+ struct null_args nargs;
+
+ memset(&nargs, 0, sizeof(nargs));
+ nargs.nulla_target = __UNCONST(what);
+ if (rump_sys_mount(MOUNT_NULL, mp, flags, &nargs, sizeof(nargs)) == -1)
+ atf_tc_fail_errno("could not mount nullfs");
+
+}
+
+ATF_TC_BODY(basic, tc)
+{
+ struct tmpfs_args targs;
+ struct stat sb;
+ int error;
+
+ rump_init();
+ if (rump_sys_mkdir("/td1", 0777) == -1)
+ atf_tc_fail_errno("mp1");
+ if (rump_sys_mkdir("/td2", 0777) == -1)
+ atf_tc_fail_errno("mp1");
+
+ /* use tmpfs because rumpfs doesn't support regular files */
+ memset(&targs, 0, sizeof(targs));
+ targs.ta_version = TMPFS_ARGS_VERSION;
+ targs.ta_root_mode = 0777;
+ if (rump_sys_mount(MOUNT_TMPFS, "/td1", 0, &targs, sizeof(targs)) == -1)
+ atf_tc_fail_errno("could not mount tmpfs td1");
+
+ mountnull("/td1", "/td2", 0);
+
+ /* test unnull -> null */
+ xput_tfile("/td1/tensti", "jeppe");
+ error = xread_tfile("/td2/tensti", "jeppe");
+ if (error != 0)
+ atf_tc_fail("null compare failed: %d (%s)",
+ error, strerror(error));
+
+ /* test null -> unnull */
+ xput_tfile("/td2/kiekko", "keppi");
+ error = xread_tfile("/td1/kiekko", "keppi");
+ if (error != 0)
+ atf_tc_fail("unnull compare failed: %d (%s)",
+ error, strerror(error));
+
+ /* test unnull -> null overwrite */
+ xput_tfile("/td1/tensti", "se oolannin sota");
+ error = xread_tfile("/td2/tensti", "se oolannin sota");
+ if (error != 0)
+ atf_tc_fail("unnull compare failed: %d (%s)",
+ error, strerror(error));
+
+ /* test that /td2 is unaffected in "real life" */
+ if (rump_sys_unmount("/td2", 0) == -1)
+ atf_tc_fail_errno("cannot unmount nullfs");
+ if ((error = rump_sys_stat("/td2/tensti", &sb)) != -1
+ || errno != ENOENT) {
+ atf_tc_fail("stat tensti should return ENOENT, got %d", error);
+ }
+ if ((error = rump_sys_stat("/td2/kiekko", &sb)) != -1
+ || errno != ENOENT) {
+ atf_tc_fail("stat kiekko should return ENOENT, got %d", error);
+ }
+
+ /* done */
+}
+
+ATF_TC(twistymount);
+ATF_TC_HEAD(twistymount, tc)
+{
+
+ /* this is expected to fail until the PR is fixed */
+ atf_tc_set_md_var(tc, "descr", "\"recursive\" mounts deadlock"
+ " (kern/43439)");
+}
+
+/*
+ * Mapping to identifiers in kern/43439:
+ * /td = /home/current/pkgsrc
+ * /td/dist = /home/current/pkgsrc/distiles
+ * /mp = /usr/pkgsrc
+ * /mp/dist = /usr/pkgsrc/distfiles -- "created" by first null mount
+ */
+
+ATF_TC_BODY(twistymount, tc)
+{
+ int mkd = 0;
+
+ rump_init();
+
+ if (rump_sys_mkdir("/td", 0777) == -1)
+ atf_tc_fail_errno("mkdir %d", mkd++);
+ if (rump_sys_mkdir("/td/dist", 0777) == -1)
+ atf_tc_fail_errno("mkdir %d", mkd++);
+ if (rump_sys_mkdir("/mp", 0777) == -1)
+ atf_tc_fail_errno("mkdir %d", mkd++);
+
+ /* MNT_RDONLY doesn't matter, but just for compat with the PR */
+ mountnull("/td", "/mp", MNT_RDONLY);
+ mountnull("/td/dist", "/mp/dist", 0);
+
+ /* if we didn't get a locking-against-meself panic, we passed */
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, basic);
+ ATF_TP_ADD_TC(tp, twistymount);
+
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/psshfs/h_have_puffs.c b/contrib/netbsd-tests/fs/psshfs/h_have_puffs.c
new file mode 100644
index 000000000000..9d349508ca97
--- /dev/null
+++ b/contrib/netbsd-tests/fs/psshfs/h_have_puffs.c
@@ -0,0 +1,58 @@
+/* $NetBSD: h_have_puffs.c,v 1.1 2010/07/06 14:06:22 pooka Exp $ */
+/*
+ * Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int
+main(void)
+{
+ int exitcode, fd;
+
+ fd = open("/dev/puffs", O_RDWR);
+ if (fd != -1) {
+ printf("yes\n");
+ close(fd);
+ exitcode = EXIT_SUCCESS;
+ } else {
+ if (errno == ENXIO) {
+ printf("enxio\n");
+ exitcode = EXIT_SUCCESS;
+ } else if (errno == EACCES) {
+ printf("eacces\n");
+ exitcode = EXIT_SUCCESS;
+ } else {
+ printf("failed\n");
+ exitcode = EXIT_FAILURE;
+ }
+ }
+
+ return exitcode;
+}
diff --git a/contrib/netbsd-tests/fs/psshfs/ssh_config.in b/contrib/netbsd-tests/fs/psshfs/ssh_config.in
new file mode 100644
index 000000000000..3ebd68b3fdac
--- /dev/null
+++ b/contrib/netbsd-tests/fs/psshfs/ssh_config.in
@@ -0,0 +1,14 @@
+# $NetBSD: ssh_config.in,v 1.1 2010/07/06 14:06:22 pooka Exp $
+
+# Basic settings.
+Port 10000
+Protocol 2
+
+# The temporary key used for login.
+IdentityFile @WORKDIR@/ssh_user_key
+
+# Prevent the client from complaining about unknown host keys.
+GlobalKnownHostsFile @WORKDIR@/known_hosts
+
+# Do not attempt password authentication in case keys fail.
+IdentitiesOnly yes
diff --git a/contrib/netbsd-tests/fs/psshfs/ssh_host_key b/contrib/netbsd-tests/fs/psshfs/ssh_host_key
new file mode 100644
index 000000000000..ebdbc8b44171
--- /dev/null
+++ b/contrib/netbsd-tests/fs/psshfs/ssh_host_key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDJnMpSG1lGmApk8F7ZH7TGtjjP/WUs+vqHyFsyS2lilJzereen
+a/ME6S0d0HTOeCdKldjbtDpfNXbh+XnJMlJMEgEs4Mg1jluuEV0GOJoMt7cGzku2
+gAYGx++2+wqYw6Y+M8Tb1M4AO+PcxD/3BkdUyIKO63v6STl2VQn1BzsTQwIBIwKB
+gAuFTWPHDGpvFols0jhK9GMgWwSSIwnidLdNRwowMehgQ3pwVmFWoCwqlN0h2sn4
+PMJu9nL0WxtiJAzprzARgQuPI25t9LiKTF7scC/cNUiHPplUjvoDXA9ccY1eIf4R
+e6wwZz1jfCWen0eRsvMyoYvFmEH8hILAk1bY9heymOGLAkEA/WhC49n+gtloVMow
+iKQOO6+u3ouxTOTQ3sV2wCaLaO2pEbHP2//5SlUJLp6QrjC7bg9Kr+f56+zT2he9
+f6GCwwJBAMus3XizmZdJyJLnkCJRiT0/3Kf57fhWKRdnFkuRLyjET9MEWavRdJmr
+bx/lxmILi1iKwXiFEDM6MqYfmNImJYECQQCtw9YYlXtSaTGZOi/oqwJyEhGCqO6b
+II85q/moVPHhjQY4BOZNttbT4on0FPV+wlSjPa+OkHDcSp/mAaaDZ2+bAkEAujel
+6rLVkaKLfv+ZuPoXE22WivMityo0Mqdk12ArHfVQS+a4YpOdzlOYzLTSosi56o19
+sAShGOTAl+Jf1hQ/iwJAKpPviX5w292H/m5T0m4l0NRdQ3pRujOLMSVmY+/HFZTW
+GJMYLr1eBKNfLsKzJgB88GzuF2O/O8hNi3XSiOP+9w==
+-----END RSA PRIVATE KEY-----
diff --git a/contrib/netbsd-tests/fs/psshfs/ssh_host_key.pub b/contrib/netbsd-tests/fs/psshfs/ssh_host_key.pub
new file mode 100644
index 000000000000..8d087959c4ab
--- /dev/null
+++ b/contrib/netbsd-tests/fs/psshfs/ssh_host_key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAyZzKUhtZRpgKZPBe2R+0xrY4z/1lLPr6h8hbMktpYpSc3q3np2vzBOktHdB0zngnSpXY27Q6XzV24fl5yTJSTBIBLODINY5brhFdBjiaDLe3Bs5LtoAGBsfvtvsKmMOmPjPE29TOADvj3MQ/9wZHVMiCjut7+kk5dlUJ9Qc7E0M= test@test.example.net
diff --git a/contrib/netbsd-tests/fs/psshfs/sshd_config.in b/contrib/netbsd-tests/fs/psshfs/sshd_config.in
new file mode 100644
index 000000000000..a7f86b1d3388
--- /dev/null
+++ b/contrib/netbsd-tests/fs/psshfs/sshd_config.in
@@ -0,0 +1,40 @@
+# $NetBSD: sshd_config.in,v 1.2 2011/02/11 13:19:46 pooka Exp $
+
+# Basic settings.
+Port 10000
+Protocol 2
+
+# Provide information to the user in case something goes wrong.
+LogLevel DEBUG1
+
+# The host key. It lives in the work directory because we need to set
+# very strict permissions on it and cannot modify the copy on the source
+# directory.
+HostKey @WORKDIR@/ssh_host_key
+
+# The authorized keys file we set up during the test to allow the client
+# to safely log in. We need to disable strict modes because ATF_WORKDIR
+# usually lives in /tmp, which has 1777 permissions and are not liked by
+# sshd.
+AuthorizedKeysFile @WORKDIR@/authorized_keys
+StrictModes no
+
+# Some settings to allow user runs of sshd.
+PidFile @WORKDIR@/sshd.pid
+Subsystem sftp @WORKDIR@/sftp-server
+UsePam no
+UsePrivilegeSeparation no
+
+# The root user should also be able to run the tests.
+PermitRootLogin yes
+
+# Be restrictive about access to the temporary server. Only allow key-based
+# authentication.
+ChallengeResponseAuthentication no
+GSSAPIAuthentication no
+HostbasedAuthentication no
+KerberosAuthentication no
+MaxAuthTries 1
+MaxStartups 1
+PasswordAuthentication no
+PubkeyAuthentication yes
diff --git a/contrib/netbsd-tests/fs/psshfs/t_psshfs.sh b/contrib/netbsd-tests/fs/psshfs/t_psshfs.sh
new file mode 100755
index 000000000000..4d8fede15494
--- /dev/null
+++ b/contrib/netbsd-tests/fs/psshfs/t_psshfs.sh
@@ -0,0 +1,295 @@
+# $NetBSD: t_psshfs.sh,v 1.8 2016/09/05 08:53:57 christos Exp $
+#
+# Copyright (c) 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+# -------------------------------------------------------------------------
+# Auxiliary functions.
+# -------------------------------------------------------------------------
+
+#
+# Skips the calling test case if puffs is not supported in the kernel
+# or if the calling user does not have the necessary permissions to mount
+# file systems.
+#
+require_puffs() {
+ case "$($(atf_get_srcdir)/h_have_puffs)" in
+ eacces)
+ atf_skip "Cannot open /dev/puffs for read/write access"
+ ;;
+ enxio)
+ atf_skip "puffs support not built into the kernel"
+ ;;
+ failed)
+ atf_skip "Unknown error trying to access /dev/puffs"
+ ;;
+ yes)
+ ;;
+ *)
+ atf_fail "Unknown value returned by h_have_puffs"
+ ;;
+ esac
+
+ if [ $(id -u) -ne 0 -a $(sysctl -n vfs.generic.usermount) -eq 0 ]
+ then
+ atf_skip "Regular users cannot mount file systems" \
+ "(vfs.generic.usermount is set to 0)"
+ fi
+}
+
+#
+# Starts a SSH server and sets up the client to access it.
+# Authentication is allowed and done using an RSA key exclusively, which
+# is generated on the fly as part of the test case.
+# XXX: Ideally, all the tests in this test program should be able to share
+# the generated key, because creating it can be a very slow process on some
+# machines.
+#
+start_ssh() {
+ echo "Setting up SSH server configuration"
+ sed -e "s,@SRCDIR@,$(atf_get_srcdir),g" -e "s,@WORKDIR@,$(pwd),g" \
+ $(atf_get_srcdir)/sshd_config.in >sshd_config || \
+ atf_fail "Failed to create sshd_config"
+ atf_check -s eq:0 -o empty -e empty cp /usr/libexec/sftp-server .
+ atf_check -s eq:0 -o empty -e empty \
+ cp $(atf_get_srcdir)/ssh_host_key .
+ atf_check -s eq:0 -o empty -e empty \
+ cp $(atf_get_srcdir)/ssh_host_key.pub .
+ atf_check -s eq:0 -o empty -e empty chmod 400 ssh_host_key
+ atf_check -s eq:0 -o empty -e empty chmod 444 ssh_host_key.pub
+
+ /usr/sbin/sshd -e -f ./sshd_config >sshd.log 2>&1 &
+ while [ ! -f sshd.pid ]; do
+ sleep 0.01
+ done
+ echo "SSH server started (pid $(cat sshd.pid))"
+
+ echo "Setting up SSH client configuration"
+ atf_check -s eq:0 -o empty -e empty \
+ ssh-keygen -f ssh_user_key -t rsa -b 1024 -N "" -q
+ atf_check -s eq:0 -o empty -e empty \
+ cp ssh_user_key.pub authorized_keys
+ echo "[localhost]:10000,[127.0.0.1]:10000,[::1]:10000" \
+ "$(cat $(atf_get_srcdir)/ssh_host_key.pub)" >known_hosts || \
+ atf_fail "Failed to create known_hosts"
+ atf_check -s eq:0 -o empty -e empty chmod 600 authorized_keys
+ sed -e "s,@SRCDIR@,$(atf_get_srcdir),g" -e "s,@WORKDIR@,$(pwd),g" \
+ $(atf_get_srcdir)/ssh_config.in >ssh_config || \
+ atf_fail "Failed to create ssh_config"
+}
+
+#
+# Stops the SSH server spawned by start_ssh and prints diagnosis data.
+#
+stop_ssh() {
+ if [ -f sshd.pid ]; then
+ echo "Stopping SSH server (pid $(cat sshd.pid))"
+ kill $(cat sshd.pid)
+ fi
+ if [ -f sshd.log ]; then
+ echo "Server output was:"
+ sed -e 's,^, ,' sshd.log
+ fi
+}
+
+#
+# Mounts the given source directory on the target directory using psshfs.
+# Both directories are supposed to live on the current directory.
+#
+mount_psshfs() {
+ atf_check -s eq:0 -o empty -e empty \
+ mount -t psshfs -o -F=$(pwd)/ssh_config localhost:$(pwd)/${1} ${2}
+}
+
+# -------------------------------------------------------------------------
+# The test cases.
+# -------------------------------------------------------------------------
+
+atf_test_case inode_nos cleanup
+inode_nos_head() {
+ atf_set "descr" "Checks that different files get different inode" \
+ "numbers"
+}
+inode_nos_body() {
+ require_puffs
+
+ start_ssh
+
+ mkdir root
+ mkdir root/dir
+ touch root/dir/file1
+ touch root/dir/file2
+ touch root/file3
+ touch root/file4
+
+ cat >ne_inodes.sh <<EOF
+#! /bin/sh
+#
+# Compares the inodes of the two given files and returns true if they are
+# different; false otherwise.
+#
+set -e
+ino1=\$(stat -f %i \${1})
+ino2=\$(stat -f %i \${2})
+test \${ino1} -ne \${ino2}
+EOF
+ chmod +x ne_inodes.sh
+
+ mkdir mnt
+ mount_psshfs root mnt
+ atf_check -s eq:0 -o empty -e empty \
+ ./ne_inodes.sh root/dir root/dir/file1
+ atf_check -s eq:0 -o empty -e empty \
+ ./ne_inodes.sh root/dir root/dir/file2
+ atf_check -s eq:0 -o empty -e empty \
+ ./ne_inodes.sh root/dir/file1 root/dir/file2
+ atf_check -s eq:0 -o empty -e empty \
+ ./ne_inodes.sh root/file3 root/file4
+}
+inode_nos_cleanup() {
+ umount mnt
+ stop_ssh
+}
+
+atf_test_case pwd cleanup
+pwd_head() {
+ atf_set "descr" "Checks that pwd works correctly"
+}
+pwd_body() {
+ require_puffs
+
+ start_ssh
+
+ mkdir root
+ mkdir root/dir
+
+ mkdir mnt
+ atf_check -s eq:0 -o save:stdout -e empty \
+ -x 'echo $(cd mnt && /bin/pwd)/dir'
+ mv stdout expout
+ mount_psshfs root mnt
+ atf_check -s eq:0 -o file:expout -e empty \
+ -x 'cd mnt/dir && ls .. >/dev/null && /bin/pwd'
+}
+pwd_cleanup() {
+ umount mnt
+ stop_ssh
+}
+
+atf_test_case ls cleanup
+ls_head() {
+ atf_set "descr" "Uses ls, attempts to exercise puffs_cc"
+}
+ls_body() {
+ require_puffs
+
+ start_ssh
+
+ mkdir mnt
+ mkdir root
+ mkdir root/dir
+ touch root/dir/file1
+ touch root/dir/file2
+ touch root/file3
+ touch root/file4
+
+ mount_psshfs root mnt
+
+ ls -l mnt &
+
+ IFS=' '
+lsout='dir
+file3
+file4
+
+mnt/dir:
+file1
+file2
+'
+ atf_check -s exit:0 -o inline:"$lsout" ls -R mnt
+}
+ls_cleanup() {
+ umount mnt
+ stop_ssh
+}
+
+atf_test_case setattr_cache cleanup
+setattr_cache_head() {
+ atf_set "descr" "Checks that setattr caches"
+ # Don't wait for the eternity that atf usually waits. Twenty
+ # seconds should be good enough, except maybe on a VAX...
+ atf_set "timeout" 20
+}
+setattr_cache_body() {
+ require_puffs
+ start_ssh
+ atf_check -s exit:0 mkdir root
+ atf_check -s exit:0 mkdir mnt
+ mount_psshfs root mnt
+ atf_check -s exit:0 -x ': > mnt/loser'
+ atf_check -s exit:0 -o save:stat stat mnt/loser
+ # Oops -- this doesn't work. We need to stop the child of the
+ # sshd that is handling the sftp session.
+ atf_check -s exit:0 kill -STOP $(cat sshd.pid)
+ atf_check -s exit:0 -x ': > mnt/loser'
+ atf_check -s exit:0 -o file:stat stat mnt/loser
+}
+setattr_cache_cleanup() {
+ umount mnt
+ kill -CONT $(cat sshd.pid)
+ stop_ssh
+}
+
+atf_test_case read_empty_file cleanup
+read_empty_file_head() {
+ atf_set "descr" "Checks whether an empty file can be read"
+ # This test is supposed to make sure psshfs does not hang
+ # when reading from an empty file, hence the timeout.
+ atf_set "timeout" 8
+}
+read_empty_file_body() {
+ require_puffs
+ start_ssh
+ atf_check mkdir root mnt
+ atf_check -x ': > root/empty'
+ mount_psshfs root mnt
+ atf_check cat mnt/empty
+}
+read_empty_file_cleanup() {
+ umount mnt
+ stop_ssh
+}
+
+# -------------------------------------------------------------------------
+# Initialization.
+# -------------------------------------------------------------------------
+
+atf_init_test_cases() {
+ atf_add_test_case inode_nos
+ atf_add_test_case pwd
+ atf_add_test_case ls
+ #atf_add_test_case setattr_cache
+ atf_add_test_case read_empty_file
+}
diff --git a/contrib/netbsd-tests/fs/ptyfs/t_nullpts.c b/contrib/netbsd-tests/fs/ptyfs/t_nullpts.c
new file mode 100644
index 000000000000..fcc7e47f6215
--- /dev/null
+++ b/contrib/netbsd-tests/fs/ptyfs/t_nullpts.c
@@ -0,0 +1,128 @@
+/* $NetBSD: t_nullpts.c,v 1.6 2017/01/13 21:30:40 christos Exp $ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/ioctl.h>
+
+#include <atf-c.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include <fs/ptyfs/ptyfs.h>
+#include <miscfs/nullfs/null.h>
+
+#include "h_macros.h"
+
+static void
+mountptyfs(const char *mp, int flags)
+{
+ struct ptyfs_args args;
+
+ if (rump_sys_mkdir(mp, 0777) == -1) {
+ if (errno != EEXIST)
+ atf_tc_fail_errno("null create %s", mp);
+ }
+ memset(&args, 0, sizeof(args));
+ args.version = PTYFS_ARGSVERSION;
+ args.mode = 0777;
+ if (rump_sys_mount(MOUNT_PTYFS, mp, flags, &args, sizeof(args)) == -1)
+ atf_tc_fail_errno("could not mount ptyfs");
+}
+
+static void
+mountnull(const char *what, const char *mp, int flags)
+{
+ struct null_args nargs;
+
+ if (rump_sys_mkdir(what, 0777) == -1) {
+ if (errno != EEXIST)
+ atf_tc_fail_errno("null create %s", what);
+ }
+ if (rump_sys_mkdir(mp, 0777) == -1) {
+ if (errno != EEXIST)
+ atf_tc_fail_errno("null create %s", mp);
+ }
+ memset(&nargs, 0, sizeof(nargs));
+ nargs.nulla_target = __UNCONST(what);
+ if (rump_sys_mount(MOUNT_NULL, mp, flags, &nargs, sizeof(nargs)) == -1)
+ atf_tc_fail_errno("could not mount nullfs");
+}
+
+ATF_TC(nullrevoke);
+ATF_TC_HEAD(nullrevoke, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "null mount ptyfs and revoke");
+}
+
+ATF_TC_BODY(nullrevoke, tc)
+{
+ char path[MAXPATHLEN];
+ struct ptmget ptg;
+ int ptm;
+
+ rump_init();
+
+ /*
+ * mount /dev/pts
+ */
+ mountptyfs("/dev/pts", 0);
+
+ /*
+ * null mount /dev/pts to /null/dev/pts
+ */
+ if (rump_sys_mkdir("/null", 0777) == -1) {
+ if (errno != EEXIST)
+ atf_tc_fail_errno("null create /null");
+ }
+ if (rump_sys_mkdir("/null/dev", 0777) == -1) {
+ if (errno != EEXIST)
+ atf_tc_fail_errno("null create /null/dev");
+ }
+
+ mountnull("/dev/pts", "/null/dev/pts", 0);
+
+ /*
+ * get slave/master pair.
+ */
+ ptm = rump_sys_open("/dev/ptm", O_RDWR);
+ if (rump_sys_ioctl(ptm, TIOCPTMGET, &ptg) == -1)
+ atf_tc_fail_errno("get pty");
+
+ /*
+ * Build nullfs path to slave.
+ */
+ strcpy(path, "/null");
+ strcat(path, ptg.sn);
+
+ /*
+ * Open slave tty via nullfs.
+ */
+ if (rump_sys_open(path, O_RDWR) == -1)
+ atf_tc_fail_errno("slave null open");
+
+ /*
+ * Close slave opened with /dev/ptm. Need purely non-null refs to it.
+ */
+ rump_sys_close(ptg.sfd);
+
+ /* revoke slave tty. */
+ rump_sys_revoke(path);
+
+ /* done */
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, nullrevoke);
+
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/ptyfs/t_ptyfs.c b/contrib/netbsd-tests/fs/ptyfs/t_ptyfs.c
new file mode 100644
index 000000000000..108c344c343c
--- /dev/null
+++ b/contrib/netbsd-tests/fs/ptyfs/t_ptyfs.c
@@ -0,0 +1,62 @@
+/* $NetBSD: t_ptyfs.c,v 1.2 2017/01/13 21:30:40 christos Exp $ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <atf-c.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include <fs/ptyfs/ptyfs.h>
+
+#include "h_macros.h"
+
+static void
+mountptyfs(const char *mp, int flags)
+{
+ struct ptyfs_args args;
+
+ if (rump_sys_mkdir("/mp", 0777) == -1) {
+ if (errno != EEXIST)
+ atf_tc_fail_errno("mp1");
+ }
+ memset(&args, 0, sizeof(args));
+ args.version = PTYFS_ARGSVERSION;
+ args.mode = 0777;
+ if (rump_sys_mount(MOUNT_PTYFS, mp, flags, &args, sizeof(args)) == -1)
+ atf_tc_fail_errno("could not mount ptyfs");
+}
+
+ATF_TC(basic);
+ATF_TC_HEAD(basic, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "mount ptyfs");
+}
+
+ATF_TC_BODY(basic, tc)
+{
+
+ rump_init();
+
+ mountptyfs("/mp", 0);
+ if (rump_sys_unmount("/mp", 0) == -1)
+ atf_tc_fail_errno("unmount failed");
+
+ /* done */
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, basic);
+
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/puffs/h_dtfs/dtfs.c b/contrib/netbsd-tests/fs/puffs/h_dtfs/dtfs.c
new file mode 100644
index 000000000000..c625f90ab24b
--- /dev/null
+++ b/contrib/netbsd-tests/fs/puffs/h_dtfs/dtfs.c
@@ -0,0 +1,255 @@
+/* $NetBSD: dtfs.c,v 1.2 2010/07/21 06:58:25 pooka Exp $ */
+
+/*
+ * Copyright (c) 2006 Antti Kantee. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Delectable Test File System: a simple in-memory file system which
+ * demonstrates the use of puffs.
+ * (a.k.a. Detrempe FS ...)
+ */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <mntopts.h>
+#include <paths.h>
+#include <puffs.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dtfs.h"
+
+#ifdef DEEP_ROOTED_CLUE
+#define FSNAME "detrempe"
+#else
+#define FSNAME "dt"
+#endif
+#define MAXREQMAGIC -37
+
+static struct puffs_usermount *gpu;
+static struct dtfs_mount gdtm;
+int dynamicfh;
+int straightflush;
+
+static void usage(void);
+
+static void
+usage()
+{
+
+ fprintf(stderr, "usage: %s [-bsdftl] [-c hashbuckets] [-m maxreqsize] "
+ "[-n typename]\n [-o mntopt] [-o puffsopt] [-p prot] "
+ "[-r rootnodetype]\n detrempe /mountpoint\n", getprogname());
+ exit(1);
+}
+
+static void
+wipe_the_sleep_out_of_my_eyes(int v)
+{
+
+ gdtm.dtm_needwakeup++;
+}
+
+static void
+loopfun(struct puffs_usermount *pu)
+{
+ struct dtfs_mount *dtm = puffs_getspecific(pu);
+ struct dtfs_poll *dp;
+
+ while (dtm->dtm_needwakeup) {
+ dtm->dtm_needwakeup--;
+ dp = LIST_FIRST(&dtm->dtm_pollent);
+ if (dp == NULL)
+ return;
+
+ LIST_REMOVE(dp, dp_entries);
+ puffs_cc_continue(dp->dp_pcc);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ extern char *optarg;
+ extern int optind;
+ struct puffs_usermount *pu;
+ struct puffs_pathobj *po_root;
+ struct puffs_ops *pops;
+ struct timespec ts;
+ const char *typename;
+ char *rtstr;
+ mntoptparse_t mp;
+ int pflags, detach, mntflags;
+ int ch;
+ int khashbuckets;
+ int maxreqsize;
+
+ setprogname(argv[0]);
+
+ rtstr = NULL;
+ detach = 1;
+ mntflags = 0;
+ khashbuckets = 256;
+ pflags = PUFFS_KFLAG_IAONDEMAND;
+ typename = FSNAME;
+ maxreqsize = MAXREQMAGIC;
+ gdtm.dtm_allowprot = VM_PROT_ALL;
+ while ((ch = getopt(argc, argv, "bc:dfilm:n:o:p:r:st")) != -1) {
+ switch (ch) {
+ case 'b': /* build paths, for debugging the feature */
+ pflags |= PUFFS_FLAG_BUILDPATH;
+ break;
+ case 'c':
+ khashbuckets = atoi(optarg);
+ break;
+ case 'd':
+ dynamicfh = 1;
+ break;
+ case 'f':
+ pflags |= PUFFS_KFLAG_LOOKUP_FULLPNBUF;
+ break;
+ case 'i':
+ pflags &= ~PUFFS_KFLAG_IAONDEMAND;
+ break;
+ case 'l':
+ straightflush = 1;
+ break;
+ case 'm':
+ maxreqsize = atoi(optarg);
+ break;
+ case 'n':
+ typename = optarg;
+ break;
+ case 'o':
+ mp = getmntopts(optarg, puffsmopts, &mntflags, &pflags);
+ if (mp == NULL)
+ err(1, "getmntopts");
+ freemntopts(mp);
+ break;
+ case 'p':
+ gdtm.dtm_allowprot = atoi(optarg);
+ if ((gdtm.dtm_allowprot | VM_PROT_ALL) != VM_PROT_ALL)
+ usage();
+ break;
+ case 'r':
+ rtstr = optarg;
+ break;
+ case 's': /* stay on top */
+ detach = 0;
+ break;
+ case 't':
+ pflags |= PUFFS_KFLAG_WTCACHE;
+ break;
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+ }
+ if (pflags & PUFFS_FLAG_OPDUMP)
+ detach = 0;
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ PUFFSOP_INIT(pops);
+
+ PUFFSOP_SET(pops, dtfs, fs, statvfs);
+ PUFFSOP_SET(pops, dtfs, fs, unmount);
+ PUFFSOP_SETFSNOP(pops, sync);
+ PUFFSOP_SET(pops, dtfs, fs, fhtonode);
+ PUFFSOP_SET(pops, dtfs, fs, nodetofh);
+
+ PUFFSOP_SET(pops, dtfs, node, lookup);
+ PUFFSOP_SET(pops, dtfs, node, access);
+ PUFFSOP_SET(pops, puffs_genfs, node, getattr);
+ PUFFSOP_SET(pops, dtfs, node, setattr);
+ PUFFSOP_SET(pops, dtfs, node, create);
+ PUFFSOP_SET(pops, dtfs, node, remove);
+ PUFFSOP_SET(pops, dtfs, node, readdir);
+ PUFFSOP_SET(pops, dtfs, node, poll);
+ PUFFSOP_SET(pops, dtfs, node, mmap);
+ PUFFSOP_SET(pops, dtfs, node, mkdir);
+ PUFFSOP_SET(pops, dtfs, node, rmdir);
+ PUFFSOP_SET(pops, dtfs, node, rename);
+ PUFFSOP_SET(pops, dtfs, node, read);
+ PUFFSOP_SET(pops, dtfs, node, write);
+ PUFFSOP_SET(pops, dtfs, node, link);
+ PUFFSOP_SET(pops, dtfs, node, symlink);
+ PUFFSOP_SET(pops, dtfs, node, readlink);
+ PUFFSOP_SET(pops, dtfs, node, mknod);
+ PUFFSOP_SET(pops, dtfs, node, inactive);
+ PUFFSOP_SET(pops, dtfs, node, pathconf);
+ PUFFSOP_SET(pops, dtfs, node, reclaim);
+
+ srandom(time(NULL)); /* for random generation numbers */
+
+ pu = puffs_init(pops, _PATH_PUFFS, typename, &gdtm, pflags);
+ if (pu == NULL)
+ err(1, "init");
+ gpu = pu;
+
+ puffs_setfhsize(pu, sizeof(struct dtfs_fid),
+ PUFFS_FHFLAG_NFSV2 | PUFFS_FHFLAG_NFSV3
+ | (dynamicfh ? PUFFS_FHFLAG_DYNAMIC : 0));
+ puffs_setncookiehash(pu, khashbuckets);
+
+ if (signal(SIGALRM, wipe_the_sleep_out_of_my_eyes) == SIG_ERR)
+ warn("cannot set alarm sighandler");
+
+ /* init */
+ if (dtfs_domount(pu, rtstr) != 0)
+ errx(1, "dtfs_domount failed");
+
+ po_root = puffs_getrootpathobj(pu);
+ po_root->po_path = argv[0];
+ po_root->po_len = strlen(argv[0]);
+
+ /* often enough for testing poll */
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+ puffs_ml_setloopfn(pu, loopfun);
+ puffs_ml_settimeout(pu, &ts);
+
+ if (maxreqsize != MAXREQMAGIC)
+ puffs_setmaxreqlen(pu, maxreqsize);
+
+ puffs_set_errnotify(pu, puffs_kernerr_abort);
+ if (detach)
+ if (puffs_daemon(pu, 1, 1) == -1)
+ err(1, "puffs_daemon");
+
+ if (puffs_mount(pu, argv[1], mntflags, puffs_getroot(pu)) == -1)
+ err(1, "mount");
+ if (puffs_mainloop(pu) == -1)
+ err(1, "mainloop");
+
+ return 0;
+}
diff --git a/contrib/netbsd-tests/fs/puffs/h_dtfs/dtfs.h b/contrib/netbsd-tests/fs/puffs/h_dtfs/dtfs.h
new file mode 100644
index 000000000000..de4d9589e5e1
--- /dev/null
+++ b/contrib/netbsd-tests/fs/puffs/h_dtfs/dtfs.h
@@ -0,0 +1,127 @@
+/* $NetBSD: dtfs.h,v 1.2 2010/07/14 13:09:52 pooka Exp $ */
+
+/*
+ * Copyright (c) 2006 Antti Kantee. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef DTFS_H_
+#define DTFS_H_
+
+#include <sys/types.h>
+
+#include <puffs.h>
+
+PUFFSOP_PROTOS(dtfs);
+int dtfs_domount(struct puffs_usermount *, const char *);
+
+#define DTFS_BLOCKSHIFT (12)
+#define DTFS_BLOCKSIZE (1<<DTFS_BLOCKSHIFT)
+
+#define ROUNDUP(a,b) ((a) & ((b)-1))
+#define BLOCKNUM(a,b) (((a) & ~((1<<(b))-1)) >> (b))
+
+struct dtfs_fid;
+struct dtfs_mount {
+ ino_t dtm_nextfileid; /* running number for file id */
+
+ size_t dtm_fsizes; /* sum of file sizes in bytes */
+ fsfilcnt_t dtm_nfiles; /* number of files */
+
+ LIST_HEAD(, dtfs_poll) dtm_pollent;
+ int dtm_needwakeup;
+ vm_prot_t dtm_allowprot;
+};
+
+struct dtfs_file {
+ union {
+ struct {
+ uint8_t **blocks;
+ size_t numblocks;
+ size_t datalen;
+ } reg;
+ struct {
+ struct puffs_node *dotdot;
+ LIST_HEAD(, dtfs_dirent) dirents;
+ } dir;
+ struct {
+ char *target;
+ } link;
+ } u;
+#define df_blocks u.reg.blocks
+#define df_numblocks u.reg.numblocks
+#define df_datalen u.reg.datalen
+#define df_dotdot u.dir.dotdot
+#define df_dirents u.dir.dirents
+#define df_linktarget u.link.target
+};
+
+struct dtfs_dirent {
+ struct puffs_node *dfd_node;
+ struct puffs_node *dfd_parent;
+ char *dfd_name;
+ size_t dfd_namelen;
+
+ LIST_ENTRY(dtfs_dirent) dfd_entries;
+};
+
+struct dtfs_fid {
+ struct puffs_node *dfid_addr;
+
+ /* best^Wsome-effort extra sanity check */
+ ino_t dfid_fileid;
+ u_long dfid_gen;
+};
+#define DTFS_FIDSIZE (sizeof(struct dtfs_fid))
+
+struct dtfs_poll {
+ struct puffs_cc *dp_pcc;
+ LIST_ENTRY(dtfs_poll) dp_entries;
+};
+
+struct puffs_node * dtfs_genfile(struct puffs_node *,
+ const struct puffs_cn *, enum vtype);
+struct dtfs_file * dtfs_newdir(void);
+struct dtfs_file * dtfs_newfile(void);
+struct dtfs_dirent * dtfs_dirgetnth(struct dtfs_file *, int);
+struct dtfs_dirent * dtfs_dirgetbyname(struct dtfs_file *,
+ const char *, size_t);
+
+void dtfs_nukenode(struct puffs_node *, struct puffs_node *,
+ const char *, size_t);
+void dtfs_freenode(struct puffs_node *);
+void dtfs_setsize(struct puffs_node *, off_t);
+
+void dtfs_adddent(struct puffs_node *, struct dtfs_dirent *);
+void dtfs_removedent(struct puffs_node *, struct dtfs_dirent *);
+
+void dtfs_baseattrs(struct vattr *, enum vtype, ino_t);
+void dtfs_updatetimes(struct puffs_node *, int, int, int);
+
+bool dtfs_isunder(struct puffs_node *, struct puffs_node *);
+
+
+#define DTFS_CTOF(a) ((struct dtfs_file *)(((struct puffs_node *)a)->pn_data))
+#define DTFS_PTOF(a) ((struct dtfs_file *)(a->pn_data))
+
+#endif /* DTFS_H_ */
diff --git a/contrib/netbsd-tests/fs/puffs/h_dtfs/dtfs_subr.c b/contrib/netbsd-tests/fs/puffs/h_dtfs/dtfs_subr.c
new file mode 100644
index 000000000000..fcb817680b91
--- /dev/null
+++ b/contrib/netbsd-tests/fs/puffs/h_dtfs/dtfs_subr.c
@@ -0,0 +1,358 @@
+/* $NetBSD: dtfs_subr.c,v 1.4 2013/10/19 17:45:00 christos Exp $ */
+
+/*
+ * Copyright (c) 2006 Antti Kantee. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <puffs.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "dtfs.h"
+
+void
+dtfs_baseattrs(struct vattr *vap, enum vtype type, ino_t id)
+{
+ struct timeval tv;
+ struct timespec ts;
+
+ gettimeofday(&tv, NULL);
+ TIMEVAL_TO_TIMESPEC(&tv, &ts);
+
+ vap->va_type = type;
+ if (type == VDIR) {
+ vap->va_mode = 0777;
+ vap->va_nlink = 1; /* n + 1 after adding dent */
+ } else {
+ vap->va_mode = 0666;
+ vap->va_nlink = 0; /* n + 1 */
+ }
+ vap->va_uid = 0;
+ vap->va_gid = 0;
+ vap->va_fileid = id;
+ vap->va_size = 0;
+ vap->va_blocksize = getpagesize();
+ vap->va_gen = random();
+ vap->va_flags = 0;
+ vap->va_rdev = PUFFS_VNOVAL;
+ vap->va_bytes = 0;
+ vap->va_filerev = 1;
+ vap->va_vaflags = 0;
+
+ vap->va_atime = vap->va_mtime = vap->va_ctime = vap->va_birthtime = ts;
+}
+
+/*
+ * Well, as you can probably see, this interface has the slight problem
+ * of assuming file creation will always be succesful, or at least not
+ * giving a reason for the failure. Be sure to do better when you
+ * implement your own fs.
+ */
+struct puffs_node *
+dtfs_genfile(struct puffs_node *dir, const struct puffs_cn *pcn,
+ enum vtype type)
+{
+ struct dtfs_file *dff;
+ struct dtfs_dirent *dfd;
+ struct dtfs_mount *dtm;
+ struct puffs_node *newpn;
+ uid_t uid;
+ int rv;
+
+ assert(dir->pn_va.va_type == VDIR);
+ assert(dir->pn_mnt != NULL);
+
+ uid = 0;
+ rv = puffs_cred_getuid(pcn->pcn_cred, &uid);
+ assert(rv == 0);
+
+ if (type == VDIR) {
+ dff = dtfs_newdir();
+ dff->df_dotdot = dir;
+ } else
+ dff = dtfs_newfile();
+
+ dtm = puffs_pn_getmntspecific(dir);
+ newpn = puffs_pn_new(dir->pn_mnt, dff);
+ if (newpn == NULL)
+ errx(1, "getnewpnode");
+ dtfs_baseattrs(&newpn->pn_va, type, dtm->dtm_nextfileid++);
+
+ dfd = emalloc(sizeof(struct dtfs_dirent));
+ dfd->dfd_node = newpn;
+ dfd->dfd_name = estrndup(pcn->pcn_name, pcn->pcn_namelen);
+ dfd->dfd_namelen = strlen(dfd->dfd_name);
+ dfd->dfd_parent = dir;
+ dtfs_adddent(dir, dfd);
+
+ newpn->pn_va.va_uid = uid;
+ newpn->pn_va.va_gid = dir->pn_va.va_gid;
+
+ return newpn;
+}
+
+struct dtfs_file *
+dtfs_newdir()
+{
+ struct dtfs_file *dff;
+
+ dff = emalloc(sizeof(struct dtfs_file));
+ memset(dff, 0, sizeof(struct dtfs_file));
+ LIST_INIT(&dff->df_dirents);
+
+ return dff;
+}
+
+struct dtfs_file *
+dtfs_newfile()
+{
+ struct dtfs_file *dff;
+
+ dff = emalloc(sizeof(struct dtfs_file));
+ memset(dff, 0, sizeof(struct dtfs_file));
+
+ return dff;
+}
+
+struct dtfs_dirent *
+dtfs_dirgetnth(struct dtfs_file *searchdir, int n)
+{
+ struct dtfs_dirent *dirent;
+ int i;
+
+ i = 0;
+ LIST_FOREACH(dirent, &searchdir->df_dirents, dfd_entries) {
+ if (i == n)
+ return dirent;
+ i++;
+ }
+
+ return NULL;
+}
+
+struct dtfs_dirent *
+dtfs_dirgetbyname(struct dtfs_file *searchdir, const char *fname, size_t fnlen)
+{
+ struct dtfs_dirent *dirent;
+
+ LIST_FOREACH(dirent, &searchdir->df_dirents, dfd_entries)
+ if (dirent->dfd_namelen == fnlen
+ && strncmp(dirent->dfd_name, fname, fnlen) == 0)
+ return dirent;
+
+ return NULL;
+}
+
+/*
+ * common nuke, kill dirent from parent node
+ */
+void
+dtfs_nukenode(struct puffs_node *nukeme, struct puffs_node *pn_parent,
+ const char *fname, size_t fnlen)
+{
+ struct dtfs_dirent *dfd;
+ struct dtfs_mount *dtm;
+
+ assert(pn_parent->pn_va.va_type == VDIR);
+
+ dfd = dtfs_dirgetbyname(DTFS_PTOF(pn_parent), fname, fnlen);
+ assert(dfd);
+
+ dtm = puffs_pn_getmntspecific(nukeme);
+ dtm->dtm_nfiles--;
+ assert(dtm->dtm_nfiles >= 1);
+
+ dtfs_removedent(pn_parent, dfd);
+ free(dfd);
+}
+
+/* free lingering information */
+void
+dtfs_freenode(struct puffs_node *pn)
+{
+ struct dtfs_file *df = DTFS_PTOF(pn);
+ struct dtfs_mount *dtm;
+ int i;
+
+ assert(pn->pn_va.va_nlink == 0);
+ dtm = puffs_pn_getmntspecific(pn);
+
+ switch (pn->pn_va.va_type) {
+ case VREG:
+ assert(dtm->dtm_fsizes >= pn->pn_va.va_size);
+ dtm->dtm_fsizes -= pn->pn_va.va_size;
+ for (i = 0; i < BLOCKNUM(df->df_datalen, DTFS_BLOCKSHIFT); i++)
+ free(df->df_blocks[i]);
+ if (df->df_datalen > i << DTFS_BLOCKSHIFT)
+ free(df->df_blocks[i]);
+ break;
+ case VLNK:
+ free(df->df_linktarget);
+ break;
+ case VCHR:
+ case VBLK:
+ case VDIR:
+ case VSOCK:
+ case VFIFO:
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ free(df);
+ puffs_pn_put(pn);
+}
+
+void
+dtfs_setsize(struct puffs_node *pn, off_t newsize)
+{
+ struct dtfs_file *df = DTFS_PTOF(pn);
+ struct dtfs_mount *dtm;
+ size_t newblocks;
+ int needalloc, shrinks;
+ int i;
+
+ needalloc = newsize > ROUNDUP(df->df_datalen, DTFS_BLOCKSIZE);
+ shrinks = newsize < pn->pn_va.va_size;
+
+ if (needalloc || shrinks) {
+ newblocks = BLOCKNUM(newsize, DTFS_BLOCKSHIFT) + 1;
+
+ if (shrinks)
+ for (i = newblocks; i < df->df_numblocks; i++)
+ free(df->df_blocks[i]);
+
+ df->df_blocks = erealloc(df->df_blocks,
+ newblocks * sizeof(uint8_t *));
+ /*
+ * if extended, set storage to zero
+ * to match correct behaviour
+ */
+ if (!shrinks) {
+ for (i = df->df_numblocks; i < newblocks; i++) {
+ df->df_blocks[i] = emalloc(DTFS_BLOCKSIZE);
+ memset(df->df_blocks[i], 0, DTFS_BLOCKSIZE);
+ }
+ }
+
+ df->df_datalen = newsize;
+ df->df_numblocks = newblocks;
+ }
+
+ dtm = puffs_pn_getmntspecific(pn);
+ if (!shrinks) {
+ dtm->dtm_fsizes += newsize - pn->pn_va.va_size;
+ } else {
+ dtm->dtm_fsizes -= pn->pn_va.va_size - newsize;
+ }
+
+ pn->pn_va.va_size = newsize;
+ pn->pn_va.va_bytes = BLOCKNUM(newsize,DTFS_BLOCKSHIFT)>>DTFS_BLOCKSHIFT;
+}
+
+/* add & bump link count */
+void
+dtfs_adddent(struct puffs_node *pn_dir, struct dtfs_dirent *dent)
+{
+ struct dtfs_file *dir = DTFS_PTOF(pn_dir);
+ struct puffs_node *pn_file = dent->dfd_node;
+ struct dtfs_file *file = DTFS_PTOF(pn_file);
+ struct dtfs_mount *dtm;
+
+ assert(pn_dir->pn_va.va_type == VDIR);
+ LIST_INSERT_HEAD(&dir->df_dirents, dent, dfd_entries);
+ pn_file->pn_va.va_nlink++;
+
+ dtm = puffs_pn_getmntspecific(pn_file);
+ dtm->dtm_nfiles++;
+
+ dent->dfd_parent = pn_dir;
+ if (dent->dfd_node->pn_va.va_type == VDIR) {
+ file->df_dotdot = pn_dir;
+ pn_dir->pn_va.va_nlink++;
+ }
+
+ dtfs_updatetimes(pn_dir, 0, 1, 1);
+}
+
+/* remove & lower link count */
+void
+dtfs_removedent(struct puffs_node *pn_dir, struct dtfs_dirent *dent)
+{
+ struct puffs_node *pn_file = dent->dfd_node;
+
+ assert(pn_dir->pn_va.va_type == VDIR);
+ LIST_REMOVE(dent, dfd_entries);
+ if (pn_file->pn_va.va_type == VDIR) {
+ struct dtfs_file *df = DTFS_PTOF(pn_file);
+
+ pn_dir->pn_va.va_nlink--;
+ df->df_dotdot = NULL;
+ }
+ pn_file->pn_va.va_nlink--;
+ assert(pn_dir->pn_va.va_nlink >= 2);
+
+ dtfs_updatetimes(pn_dir, 0, 1, 1);
+}
+
+void
+dtfs_updatetimes(struct puffs_node *pn, int doatime, int doctime, int domtime)
+{
+ struct timeval tv;
+ struct timespec ts;
+
+ gettimeofday(&tv, NULL);
+ TIMEVAL_TO_TIMESPEC(&tv, &ts);
+
+ if (doatime)
+ pn->pn_va.va_atime = ts;
+ if (doctime)
+ pn->pn_va.va_ctime = ts;
+ if (domtime)
+ pn->pn_va.va_mtime = ts;
+}
+
+bool
+dtfs_isunder(struct puffs_node *pn, struct puffs_node *pn_parent)
+{
+ struct dtfs_file *df;
+
+ while (pn) {
+ if (pn == pn_parent)
+ return true;
+ df = DTFS_CTOF(pn);
+ pn = df->df_dotdot;
+ }
+
+ return false;
+}
diff --git a/contrib/netbsd-tests/fs/puffs/h_dtfs/dtfs_vfsops.c b/contrib/netbsd-tests/fs/puffs/h_dtfs/dtfs_vfsops.c
new file mode 100644
index 000000000000..6f0c72ee3510
--- /dev/null
+++ b/contrib/netbsd-tests/fs/puffs/h_dtfs/dtfs_vfsops.c
@@ -0,0 +1,298 @@
+/* $NetBSD: dtfs_vfsops.c,v 1.3 2012/11/04 23:37:02 christos Exp $ */
+
+/*
+ * Copyright (c) 2006 Antti Kantee. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/resource.h>
+
+#include <stdio.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <puffs.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "dtfs.h"
+
+static int
+rtstr(struct puffs_usermount *pu, const char *str, enum vtype vt)
+{
+ struct puffs_node *pn = puffs_getroot(pu);
+ struct vattr *va = &pn->pn_va;
+ struct dtfs_file *df = DTFS_PTOF(pn);
+ char ltarg[256+1];
+
+ if (sscanf(str, "%*s %256s", ltarg) != 1)
+ return 1;
+
+ dtfs_baseattrs(va, vt, 2);
+ df->df_linktarget = estrdup(ltarg);
+
+ va->va_nlink = 1;
+ va->va_size = strlen(df->df_linktarget);
+
+ puffs_setrootinfo(pu, vt, 0, 0);
+
+ return 0;
+}
+
+static int
+rtdev(struct puffs_usermount *pu, const char *str, enum vtype vt)
+{
+ struct puffs_node *pn = puffs_getroot(pu);
+ struct vattr *va = &pn->pn_va;
+ int major, minor;
+
+ if (sscanf(str, "%*s %d %d", &major, &minor) != 2)
+ return 1;
+
+ dtfs_baseattrs(va, vt, 2);
+ va->va_nlink = 1;
+ va->va_rdev = makedev(major, minor);
+
+ if (vt == VBLK)
+ va->va_mode |= S_IFBLK;
+ else
+ va->va_mode |= S_IFCHR;
+
+ puffs_setrootinfo(pu, vt, 0, va->va_rdev);
+
+ return 0;
+}
+
+static int
+rtnorm(struct puffs_usermount *pu, const char *str, enum vtype vt)
+{
+ struct puffs_node *pn = puffs_getroot(pu);
+ struct vattr *va = &pn->pn_va;
+
+ dtfs_baseattrs(va, vt, 2);
+ if (vt == VDIR)
+ va->va_nlink = 2;
+ else
+ va->va_nlink = 1;
+
+ puffs_setrootinfo(pu, vt, 0, 0);
+
+ return 0;
+}
+
+struct rtype {
+ char *tstr;
+ enum vtype vt;
+ int (*pfunc)(struct puffs_usermount *, const char *, enum vtype);
+} rtypes[] = {
+ { "reg", VREG, rtnorm },
+ { "dir", VDIR, rtnorm },
+ { "blk", VBLK, rtdev },
+ { "chr", VCHR, rtdev },
+ { "lnk", VLNK, rtstr },
+ { "sock", VSOCK, rtnorm },
+ { "fifo", VFIFO, rtnorm }
+};
+#define NTYPES (sizeof(rtypes) / sizeof(rtypes[0]))
+
+int
+dtfs_domount(struct puffs_usermount *pu, const char *typestr)
+{
+ struct dtfs_mount *dtm;
+ struct dtfs_file *dff;
+ struct puffs_node *pn;
+ int i;
+
+ /* create mount-local thingie */
+ dtm = puffs_getspecific(pu);
+ dtm->dtm_nextfileid = 3;
+ dtm->dtm_nfiles = 1;
+ dtm->dtm_fsizes = 0;
+ LIST_INIT(&dtm->dtm_pollent);
+
+ /*
+ * create root directory, do it "by hand" to avoid special-casing
+ * dtfs_genfile()
+ */
+ dff = dtfs_newdir();
+ dff->df_dotdot = NULL;
+ pn = puffs_pn_new(pu, dff);
+ if (!pn)
+ errx(1, "puffs_newpnode");
+ puffs_setroot(pu, pn);
+
+ if (!typestr) {
+ rtnorm(pu, NULL, VDIR);
+ } else {
+ for (i = 0; i < NTYPES; i++) {
+ if (strncmp(rtypes[i].tstr, typestr,
+ strlen(rtypes[i].tstr)) == 0) {
+ if (rtypes[i].pfunc(pu, typestr,
+ rtypes[i].vt) != 0) {
+ fprintf(stderr, "failed to parse "
+ "\"%s\"\n", typestr);
+ return 1;
+ }
+ break;
+ }
+ }
+ if (i == NTYPES) {
+ fprintf(stderr, "no maching type for %s\n", typestr);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * statvfs() should fill in the following members of struct statvfs:
+ *
+ * unsigned long f_bsize; file system block size
+ * unsigned long f_frsize; fundamental file system block size
+ * unsigned long f_iosize; optimal file system block size
+ * fsblkcnt_t f_blocks; number of blocks in file system,
+ * (in units of f_frsize)
+ *
+ * fsblkcnt_t f_bfree; free blocks avail in file system
+ * fsblkcnt_t f_bavail; free blocks avail to non-root
+ * fsblkcnt_t f_bresvd; blocks reserved for root
+ *
+ * fsfilcnt_t f_files; total file nodes in file system
+ * fsfilcnt_t f_ffree; free file nodes in file system
+ * fsfilcnt_t f_favail; free file nodes avail to non-root
+ * fsfilcnt_t f_fresvd; file nodes reserved for root
+ *
+ *
+ * The rest are filled in by the kernel.
+ */
+#define ROUND(a,b) (((a) + ((b)-1)) & ~((b)-1))
+#define NFILES 1024*1024
+int
+dtfs_fs_statvfs(struct puffs_usermount *pu, struct statvfs *sbp)
+{
+ struct rlimit rlim;
+ struct dtfs_mount *dtm;
+ off_t btot, bfree;
+ int pgsize;
+
+ dtm = puffs_getspecific(pu);
+ pgsize = getpagesize();
+ memset(sbp, 0, sizeof(struct statvfs));
+
+ /*
+ * Use datasize rlimit as an _approximation_ for free size.
+ * This, of course, is not accurate due to overhead and not
+ * accounting for metadata.
+ */
+ if (getrlimit(RLIMIT_DATA, &rlim) == 0)
+ btot = rlim.rlim_cur;
+ else
+ btot = 16*1024*1024;
+ bfree = btot - dtm->dtm_fsizes;
+
+ sbp->f_blocks = ROUND(btot, pgsize) / pgsize;
+ sbp->f_files = NFILES;
+
+ sbp->f_bsize = sbp->f_frsize = sbp->f_iosize = pgsize;
+ sbp->f_bfree = sbp->f_bavail = ROUND(bfree, pgsize) / pgsize;
+ sbp->f_ffree = sbp->f_favail = NFILES - dtm->dtm_nfiles;
+
+ sbp->f_bresvd = sbp->f_fresvd = 0;
+
+ return 0;
+}
+#undef ROUND
+
+static void *
+addrcmp(struct puffs_usermount *pu, struct puffs_node *pn, void *arg)
+{
+
+ if (pn == arg)
+ return pn;
+
+ return NULL;
+}
+
+int
+dtfs_fs_fhtonode(struct puffs_usermount *pu, void *fid, size_t fidsize,
+ struct puffs_newinfo *pni)
+{
+ struct dtfs_fid *dfid;
+ struct puffs_node *pn;
+
+ assert(fidsize == sizeof(struct dtfs_fid));
+ dfid = fid;
+
+ pn = puffs_pn_nodewalk(pu, addrcmp, dfid->dfid_addr);
+ if (pn == NULL)
+ return ESTALE;
+
+ if (pn->pn_va.va_fileid != dfid->dfid_fileid
+ || pn->pn_va.va_gen != dfid->dfid_gen)
+ return ESTALE;
+
+ puffs_newinfo_setcookie(pni, pn);
+ puffs_newinfo_setvtype(pni, pn->pn_va.va_type);
+ puffs_newinfo_setsize(pni, pn->pn_va.va_size);
+ puffs_newinfo_setrdev(pni, pn->pn_va.va_rdev);
+
+ return 0;
+}
+
+int
+dtfs_fs_nodetofh(struct puffs_usermount *pu, void *cookie,
+ void *fid, size_t *fidsize)
+{
+ struct puffs_node *pn = cookie;
+ struct dtfs_fid *dfid;
+ extern int dynamicfh;
+
+ if (dynamicfh == 0) {
+ assert(*fidsize >= sizeof(struct dtfs_fid));
+ } else {
+ if (*fidsize < sizeof(struct dtfs_fid)) {
+ *fidsize = sizeof(struct dtfs_fid);
+ return E2BIG;
+ }
+ *fidsize = sizeof(struct dtfs_fid);
+ }
+
+ dfid = fid;
+
+ dfid->dfid_addr = pn;
+ dfid->dfid_fileid = pn->pn_va.va_fileid;
+ dfid->dfid_gen = pn->pn_va.va_gen;
+
+ return 0;
+}
+
+int
+dtfs_fs_unmount(struct puffs_usermount *pu, int flags)
+{
+
+ return 0;
+}
diff --git a/contrib/netbsd-tests/fs/puffs/h_dtfs/dtfs_vnops.c b/contrib/netbsd-tests/fs/puffs/h_dtfs/dtfs_vnops.c
new file mode 100644
index 000000000000..875109cb35b6
--- /dev/null
+++ b/contrib/netbsd-tests/fs/puffs/h_dtfs/dtfs_vnops.c
@@ -0,0 +1,586 @@
+/* $NetBSD: dtfs_vnops.c,v 1.10 2013/10/19 17:45:00 christos Exp $ */
+
+/*
+ * Copyright (c) 2006 Antti Kantee. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/poll.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <puffs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "dtfs.h"
+
+int
+dtfs_node_lookup(struct puffs_usermount *pu, void *opc,
+ struct puffs_newinfo *pni, const struct puffs_cn *pcn)
+{
+ struct puffs_node *pn_dir = opc;
+ struct dtfs_file *df = DTFS_CTOF(opc);
+ struct dtfs_dirent *dfd;
+ extern int straightflush;
+ int rv;
+
+ /* parent dir? */
+ if (PCNISDOTDOT(pcn)) {
+ if (df->df_dotdot == NULL)
+ return ENOENT;
+
+ assert(df->df_dotdot->pn_va.va_type == VDIR);
+ puffs_newinfo_setcookie(pni, df->df_dotdot);
+ puffs_newinfo_setvtype(pni, df->df_dotdot->pn_va.va_type);
+
+ return 0;
+ }
+
+ dfd = dtfs_dirgetbyname(df, pcn->pcn_name, pcn->pcn_namelen);
+ if (dfd) {
+ if ((pcn->pcn_flags & NAMEI_ISLASTCN) &&
+ (pcn->pcn_nameiop == NAMEI_DELETE)) {
+ rv = puffs_access(VDIR, pn_dir->pn_va.va_mode,
+ pn_dir->pn_va.va_uid, pn_dir->pn_va.va_gid,
+ PUFFS_VWRITE, pcn->pcn_cred);
+ if (rv)
+ return rv;
+ }
+ puffs_newinfo_setcookie(pni, dfd->dfd_node);
+ puffs_newinfo_setvtype(pni, dfd->dfd_node->pn_va.va_type);
+ puffs_newinfo_setsize(pni, dfd->dfd_node->pn_va.va_size);
+ puffs_newinfo_setrdev(pni, dfd->dfd_node->pn_va.va_rdev);
+
+ if (straightflush)
+ puffs_flush_pagecache_node(pu, dfd->dfd_node);
+
+ return 0;
+ }
+
+ if ((pcn->pcn_flags & NAMEI_ISLASTCN)
+ && (pcn->pcn_nameiop == NAMEI_CREATE ||
+ pcn->pcn_nameiop == NAMEI_RENAME)) {
+ rv = puffs_access(VDIR, pn_dir->pn_va.va_mode,
+ pn_dir->pn_va.va_uid, pn_dir->pn_va.va_gid,
+ PUFFS_VWRITE, pcn->pcn_cred);
+ if (rv)
+ return rv;
+ }
+
+ return ENOENT;
+}
+
+int
+dtfs_node_access(struct puffs_usermount *pu, void *opc, int acc_mode,
+ const struct puffs_cred *pcr)
+{
+ struct puffs_node *pn = opc;
+
+ return puffs_access(pn->pn_va.va_type, pn->pn_va.va_mode,
+ pn->pn_va.va_uid, pn->pn_va.va_gid, acc_mode, pcr);
+}
+
+int
+dtfs_node_setattr(struct puffs_usermount *pu, void *opc,
+ const struct vattr *va, const struct puffs_cred *pcr)
+{
+ struct puffs_node *pn = opc;
+ int rv;
+
+ /* check permissions */
+ if (va->va_flags != PUFFS_VNOVAL)
+ return EOPNOTSUPP;
+
+ if (va->va_uid != PUFFS_VNOVAL || va->va_gid != PUFFS_VNOVAL) {
+ rv = puffs_access_chown(pn->pn_va.va_uid, pn->pn_va.va_gid,
+ va->va_uid, va->va_gid, pcr);
+ if (rv)
+ return rv;
+ }
+
+ if (va->va_mode != PUFFS_VNOVAL) {
+ rv = puffs_access_chmod(pn->pn_va.va_uid, pn->pn_va.va_gid,
+ pn->pn_va.va_type, va->va_mode, pcr);
+ if (rv)
+ return rv;
+ }
+
+ if ((va->va_atime.tv_sec != PUFFS_VNOVAL
+ && va->va_atime.tv_nsec != PUFFS_VNOVAL)
+ || (va->va_mtime.tv_sec != PUFFS_VNOVAL
+ && va->va_mtime.tv_nsec != PUFFS_VNOVAL)) {
+ rv = puffs_access_times(pn->pn_va.va_uid, pn->pn_va.va_gid,
+ pn->pn_va.va_mode, va->va_vaflags & VA_UTIMES_NULL, pcr);
+ if (rv)
+ return rv;
+ }
+
+ if (va->va_size != PUFFS_VNOVAL) {
+ switch (pn->pn_va.va_type) {
+ case VREG:
+ dtfs_setsize(pn, va->va_size);
+ pn->pn_va.va_bytes = va->va_size;
+ break;
+ case VBLK:
+ case VCHR:
+ case VFIFO:
+ break;
+ case VDIR:
+ return EISDIR;
+ default:
+ return EOPNOTSUPP;
+ }
+ }
+
+ puffs_setvattr(&pn->pn_va, va);
+
+ return 0;
+}
+
+/* create a new node in the parent directory specified by opc */
+int
+dtfs_node_create(struct puffs_usermount *pu, void *opc,
+ struct puffs_newinfo *pni, const struct puffs_cn *pcn,
+ const struct vattr *va)
+{
+ struct puffs_node *pn_parent = opc;
+ struct puffs_node *pn_new;
+
+ if (!(va->va_type == VREG || va->va_type == VSOCK))
+ return ENODEV;
+
+ pn_new = dtfs_genfile(pn_parent, pcn, va->va_type);
+ puffs_setvattr(&pn_new->pn_va, va);
+
+ puffs_newinfo_setcookie(pni, pn_new);
+
+ return 0;
+}
+
+int
+dtfs_node_remove(struct puffs_usermount *pu, void *opc, void *targ,
+ const struct puffs_cn *pcn)
+{
+ struct puffs_node *pn_parent = opc;
+ struct puffs_node *pn = targ;
+
+ if (pn->pn_va.va_type == VDIR)
+ return EPERM;
+
+ dtfs_nukenode(targ, pn_parent, pcn->pcn_name, pcn->pcn_namelen);
+
+ if (pn->pn_va.va_nlink == 0)
+ puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
+
+ return 0;
+}
+
+int
+dtfs_node_mkdir(struct puffs_usermount *pu, void *opc,
+ struct puffs_newinfo *pni, const struct puffs_cn *pcn,
+ const struct vattr *va)
+{
+ struct puffs_node *pn_parent = opc;
+ struct puffs_node *pn_new;
+
+ pn_new = dtfs_genfile(pn_parent, pcn, VDIR);
+ puffs_setvattr(&pn_new->pn_va, va);
+
+ puffs_newinfo_setcookie(pni, pn_new);
+
+ return 0;
+}
+
+int
+dtfs_node_rmdir(struct puffs_usermount *pu, void *opc, void *targ,
+ const struct puffs_cn *pcn)
+{
+ struct puffs_node *pn_parent = opc;
+ struct dtfs_file *df = DTFS_CTOF(targ);
+
+ if (!LIST_EMPTY(&df->df_dirents))
+ return ENOTEMPTY;
+
+ dtfs_nukenode(targ, pn_parent, pcn->pcn_name, pcn->pcn_namelen);
+ puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
+
+ return 0;
+}
+
+int
+dtfs_node_readdir(struct puffs_usermount *pu, void *opc,
+ struct dirent *dent, off_t *readoff, size_t *reslen,
+ const struct puffs_cred *pcr,
+ int *eofflag, off_t *cookies, size_t *ncookies)
+{
+ struct puffs_node *pn = opc;
+ struct puffs_node *pn_nth;
+ struct dtfs_dirent *dfd_nth;
+
+ if (pn->pn_va.va_type != VDIR)
+ return ENOTDIR;
+
+ dtfs_updatetimes(pn, 1, 0, 0);
+
+ *ncookies = 0;
+ again:
+ if (*readoff == DENT_DOT || *readoff == DENT_DOTDOT) {
+ puffs_gendotdent(&dent, pn->pn_va.va_fileid, *readoff, reslen);
+ (*readoff)++;
+ PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff);
+ goto again;
+ }
+
+ for (;;) {
+ dfd_nth = dtfs_dirgetnth(pn->pn_data, DENT_ADJ(*readoff));
+ if (!dfd_nth) {
+ *eofflag = 1;
+ break;
+ }
+ pn_nth = dfd_nth->dfd_node;
+
+ if (!puffs_nextdent(&dent, dfd_nth->dfd_name,
+ pn_nth->pn_va.va_fileid,
+ puffs_vtype2dt(pn_nth->pn_va.va_type),
+ reslen))
+ break;
+
+ (*readoff)++;
+ PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff);
+ }
+
+ return 0;
+}
+
+int
+dtfs_node_poll(struct puffs_usermount *pu, void *opc, int *events)
+{
+ struct dtfs_mount *dtm = puffs_getspecific(pu);
+ struct dtfs_poll dp;
+ struct itimerval it;
+
+ memset(&it, 0, sizeof(struct itimerval));
+ it.it_value.tv_sec = 4;
+ if (setitimer(ITIMER_REAL, &it, NULL) == -1)
+ return errno;
+
+ dp.dp_pcc = puffs_cc_getcc(pu);
+ LIST_INSERT_HEAD(&dtm->dtm_pollent, &dp, dp_entries);
+ puffs_cc_yield(dp.dp_pcc);
+
+ *events = *events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM);
+ return 0;
+}
+
+int
+dtfs_node_mmap(struct puffs_usermount *pu, void *opc, vm_prot_t prot,
+ const struct puffs_cred *pcr)
+{
+ struct dtfs_mount *dtm = puffs_getspecific(pu);
+
+ if ((dtm->dtm_allowprot & prot) != prot)
+ return EACCES;
+
+ return 0;
+}
+
+int
+dtfs_node_rename(struct puffs_usermount *pu, void *opc, void *src,
+ const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
+ const struct puffs_cn *pcn_targ)
+{
+ struct dtfs_dirent *dfd_src;
+ struct dtfs_file *df_targ;
+ struct puffs_node *pn_sdir = opc;
+ struct puffs_node *pn_sfile = src;
+ struct puffs_node *pn_tdir = targ_dir;
+ struct puffs_node *pn_tfile = targ;
+
+ /* check that we don't do the old amigados trick */
+ if (pn_sfile->pn_va.va_type == VDIR) {
+ if (dtfs_isunder(pn_tdir, pn_sfile))
+ return EINVAL;
+
+ if ((pcn_src->pcn_namelen == 1 && pcn_src->pcn_name[0]=='.') ||
+ opc == src ||
+ PCNISDOTDOT(pcn_src) ||
+ PCNISDOTDOT(pcn_targ)) {
+ return EINVAL;
+ }
+ }
+
+ dfd_src = dtfs_dirgetbyname(DTFS_PTOF(pn_sdir),
+ pcn_src->pcn_name, pcn_src->pcn_namelen);
+
+ /* does it still exist, or did someone race us here? */
+ if (dfd_src == NULL) {
+ return ENOENT;
+ }
+
+ /* if there's a target file, nuke it for atomic replacement */
+ if (pn_tfile) {
+ if (pn_tfile->pn_va.va_type == VDIR) {
+ df_targ = DTFS_CTOF(pn_tfile);
+ if (!LIST_EMPTY(&df_targ->df_dirents))
+ return ENOTEMPTY;
+ }
+ dtfs_nukenode(pn_tfile, pn_tdir,
+ pcn_targ->pcn_name, pcn_targ->pcn_namelen);
+ }
+
+ /* out with the old */
+ dtfs_removedent(pn_sdir, dfd_src);
+ /* and in with the new */
+ dtfs_adddent(pn_tdir, dfd_src);
+
+ /* update name */
+ free(dfd_src->dfd_name);
+ dfd_src->dfd_name = estrndup(pcn_targ->pcn_name,pcn_targ->pcn_namelen);
+ dfd_src->dfd_namelen = strlen(dfd_src->dfd_name);
+
+ dtfs_updatetimes(src, 0, 1, 0);
+
+ return 0;
+}
+
+int
+dtfs_node_link(struct puffs_usermount *pu, void *opc, void *targ,
+ const struct puffs_cn *pcn)
+{
+ struct puffs_node *pn_dir = opc;
+ struct dtfs_dirent *dfd;
+
+ dfd = emalloc(sizeof(struct dtfs_dirent));
+ dfd->dfd_node = targ;
+ dfd->dfd_name = estrndup(pcn->pcn_name, pcn->pcn_namelen);
+ dfd->dfd_namelen = strlen(dfd->dfd_name);
+ dtfs_adddent(pn_dir, dfd);
+
+ dtfs_updatetimes(targ, 0, 1, 0);
+
+ return 0;
+}
+
+int
+dtfs_node_symlink(struct puffs_usermount *pu, void *opc,
+ struct puffs_newinfo *pni, const struct puffs_cn *pcn_src,
+ const struct vattr *va, const char *link_target)
+{
+ struct puffs_node *pn_parent = opc;
+ struct puffs_node *pn_new;
+ struct dtfs_file *df_new;
+
+ if (va->va_type != VLNK)
+ return ENODEV;
+
+ pn_new = dtfs_genfile(pn_parent, pcn_src, VLNK);
+ puffs_setvattr(&pn_new->pn_va, va);
+ df_new = DTFS_PTOF(pn_new);
+ df_new->df_linktarget = estrdup(link_target);
+ pn_new->pn_va.va_size = strlen(df_new->df_linktarget);
+
+ puffs_newinfo_setcookie(pni, pn_new);
+
+ return 0;
+}
+
+int
+dtfs_node_readlink(struct puffs_usermount *pu, void *opc,
+ const struct puffs_cred *cred, char *linkname, size_t *linklen)
+{
+ struct dtfs_file *df = DTFS_CTOF(opc);
+ struct puffs_node *pn = opc;
+
+ assert(pn->pn_va.va_type == VLNK);
+ strlcpy(linkname, df->df_linktarget, *linklen);
+ *linklen = strlen(linkname);
+
+ return 0;
+}
+
+int
+dtfs_node_mknod(struct puffs_usermount *pu, void *opc,
+ struct puffs_newinfo *pni, const struct puffs_cn *pcn,
+ const struct vattr *va)
+{
+ struct puffs_node *pn_parent = opc;
+ struct puffs_node *pn_new;
+
+ if (!(va->va_type == VBLK || va->va_type == VCHR
+ || va->va_type == VFIFO))
+ return EINVAL;
+
+ pn_new = dtfs_genfile(pn_parent, pcn, va->va_type);
+ puffs_setvattr(&pn_new->pn_va, va);
+
+ puffs_newinfo_setcookie(pni, pn_new);
+
+ return 0;
+}
+
+#define BLOCKOFF(a,b) ((a) & ((b)-1))
+#define BLOCKLEFT(a,b) ((b) - BLOCKOFF(a,b))
+
+/*
+ * Read operation, used both for VOP_READ and VOP_GETPAGES
+ */
+int
+dtfs_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf,
+ off_t offset, size_t *resid, const struct puffs_cred *pcr, int ioflag)
+{
+ struct puffs_node *pn = opc;
+ struct dtfs_file *df = DTFS_CTOF(opc);
+ quad_t xfer, origxfer;
+ uint8_t *src, *dest;
+ size_t copylen;
+
+ if (pn->pn_va.va_type != VREG)
+ return EISDIR;
+
+ xfer = MIN(*resid, df->df_datalen - offset);
+ if (xfer < 0)
+ return EINVAL;
+
+ dest = buf;
+ origxfer = xfer;
+ while (xfer > 0) {
+ copylen = MIN(xfer, BLOCKLEFT(offset, DTFS_BLOCKSIZE));
+ src = df->df_blocks[BLOCKNUM(offset, DTFS_BLOCKSHIFT)]
+ + BLOCKOFF(offset, DTFS_BLOCKSIZE);
+ memcpy(dest, src, copylen);
+ offset += copylen;
+ dest += copylen;
+ xfer -= copylen;
+ }
+ *resid -= origxfer;
+
+ dtfs_updatetimes(pn, 1, 0, 0);
+
+ return 0;
+}
+
+/*
+ * write operation on the wing
+ */
+int
+dtfs_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf,
+ off_t offset, size_t *resid, const struct puffs_cred *pcr, int ioflag)
+{
+ struct puffs_node *pn = opc;
+ struct dtfs_file *df = DTFS_CTOF(opc);
+ uint8_t *src, *dest;
+ size_t copylen;
+
+ if (pn->pn_va.va_type != VREG)
+ return EISDIR;
+
+ if (ioflag & PUFFS_IO_APPEND)
+ offset = pn->pn_va.va_size;
+
+ if (*resid + offset > pn->pn_va.va_size)
+ dtfs_setsize(pn, *resid + offset);
+
+ src = buf;
+ while (*resid > 0) {
+ int i;
+ copylen = MIN(*resid, BLOCKLEFT(offset, DTFS_BLOCKSIZE));
+ i = BLOCKNUM(offset, DTFS_BLOCKSHIFT);
+ dest = df->df_blocks[i]
+ + BLOCKOFF(offset, DTFS_BLOCKSIZE);
+ memcpy(dest, src, copylen);
+ offset += copylen;
+ dest += copylen;
+ *resid -= copylen;
+ }
+
+ dtfs_updatetimes(pn, 0, 1, 1);
+
+ return 0;
+}
+
+int
+dtfs_node_pathconf(struct puffs_usermount *pu, puffs_cookie_t opc,
+ int name, register_t *retval)
+{
+
+ switch (name) {
+ case _PC_LINK_MAX:
+ *retval = LINK_MAX;
+ return 0;
+ case _PC_NAME_MAX:
+ *retval = NAME_MAX;
+ return 0;
+ case _PC_PATH_MAX:
+ *retval = PATH_MAX;
+ return 0;
+ case _PC_PIPE_BUF:
+ *retval = PIPE_BUF;
+ return 0;
+ case _PC_CHOWN_RESTRICTED:
+ *retval = 1;
+ return 0;
+ case _PC_NO_TRUNC:
+ *retval = 1;
+ return 0;
+ case _PC_SYNC_IO:
+ *retval = 1;
+ return 0;
+ case _PC_FILESIZEBITS:
+ *retval = 43; /* this one goes to 11 */
+ return 0;
+ case _PC_SYMLINK_MAX:
+ *retval = MAXPATHLEN;
+ return 0;
+ case _PC_2_SYMLINKS:
+ *retval = 1;
+ return 0;
+ default:
+ return EINVAL;
+ }
+}
+
+int
+dtfs_node_inactive(struct puffs_usermount *pu, puffs_cookie_t opc)
+{
+ struct puffs_node *pn = opc;
+
+ if (pn->pn_va.va_nlink == 0)
+ puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N1);
+ return 0;
+}
+
+int
+dtfs_node_reclaim(struct puffs_usermount *pu, void *opc)
+{
+ struct puffs_node *pn = opc;
+
+ if (pn->pn_va.va_nlink == 0)
+ dtfs_freenode(pn);
+
+ return 0;
+}
diff --git a/contrib/netbsd-tests/fs/puffs/t_basic.c b/contrib/netbsd-tests/fs/puffs/t_basic.c
new file mode 100644
index 000000000000..4b5b7b81b2a4
--- /dev/null
+++ b/contrib/netbsd-tests/fs/puffs/t_basic.c
@@ -0,0 +1,455 @@
+/* $NetBSD: t_basic.c,v 1.14 2017/01/13 21:30:40 christos Exp $ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+
+#include <assert.h>
+#include <atf-c.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <puffs.h>
+#include <puffsdump.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_macros.h"
+#include "../common/h_fsmacros.h"
+
+/*
+ * Do a synchronous operation. When this returns, all FAF operations
+ * have at least been delivered to the file system.
+ *
+ * XXX: is this really good enough considering puffs(9)-issued
+ * callback operations?
+ */
+static void
+syncbar(const char *fs)
+{
+ struct statvfs svb;
+
+ if (rump_sys_statvfs1(fs, &svb, ST_WAIT) == -1)
+ atf_tc_fail_errno("statvfs");
+}
+
+#ifdef PUFFSDUMP
+static void __unused
+dumpopcount(struct puffstestargs *args)
+{
+ size_t i;
+
+ printf("VFS OPS:\n");
+ for (i = 0; i < MIN(puffsdump_vfsop_count, PUFFS_VFS_MAX); i++) {
+ printf("\t%s: %d\n",
+ puffsdump_vfsop_revmap[i], args->pta_vfs_toserv_ops[i]);
+ }
+
+ printf("VN OPS:\n");
+ for (i = 0; i < MIN(puffsdump_vnop_count, PUFFS_VN_MAX); i++) {
+ printf("\t%s: %d\n",
+ puffsdump_vnop_revmap[i], args->pta_vn_toserv_ops[i]);
+ }
+}
+#endif
+
+ATF_TC(mount);
+ATF_TC_HEAD(mount, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "puffs+dtfs un/mount test");
+}
+
+ATF_TC_BODY(mount, tc)
+{
+ void *args;
+
+ FSTEST_CONSTRUCTOR(tc, puffs, args);
+ FSTEST_DESTRUCTOR(tc, puffs, args);
+}
+
+ATF_TC(root_reg);
+ATF_TC_HEAD(root_reg, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "root is a regular file");
+}
+
+#define MAKEOPTS(...) \
+ char *theopts[] = {NULL, "-s", __VA_ARGS__, "dtfs", "n/a", NULL}
+
+ATF_TC_BODY(root_reg, tc)
+{
+ MAKEOPTS("-r", "reg");
+ void *args;
+ int fd, rv;
+
+ FSTEST_CONSTRUCTOR_FSPRIV(tc, puffs, args, theopts);
+
+ fd = rump_sys_open(FSTEST_MNTNAME, O_RDWR);
+ if (fd == -1)
+ atf_tc_fail_errno("open root");
+ if (rump_sys_write(fd, &fd, sizeof(fd)) != sizeof(fd))
+ atf_tc_fail_errno("write to root");
+ rv = rump_sys_mkdir(FSTEST_MNTNAME "/test", 0777);
+ ATF_REQUIRE(errno == ENOTDIR);
+ ATF_REQUIRE(rv == -1);
+ rump_sys_close(fd);
+
+ FSTEST_DESTRUCTOR(tc, puffs, args);
+}
+
+ATF_TC(root_lnk);
+ATF_TC_HEAD(root_lnk, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "root is a symbolic link");
+}
+
+#define LINKSTR "/path/to/nowhere"
+ATF_TC_BODY(root_lnk, tc)
+{
+ MAKEOPTS("-r", "lnk " LINKSTR);
+ void *args;
+ char buf[PATH_MAX];
+ ssize_t len;
+
+ FSTEST_CONSTRUCTOR_FSPRIV(tc, puffs, args, theopts);
+
+ if ((len = rump_sys_readlink(FSTEST_MNTNAME, buf, sizeof(buf)-1)) == -1)
+ atf_tc_fail_errno("readlink");
+ buf[len] = '\0';
+
+ ATF_REQUIRE_STREQ(buf, LINKSTR);
+
+#if 0 /* XXX: unmount uses FOLLOW */
+ if (rump_sys_unmount("/mp", 0) == -1)
+ atf_tc_fail_errno("unmount");
+#endif
+}
+
+ATF_TC(root_fifo);
+ATF_TC_HEAD(root_fifo, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "root is a symbolic link");
+}
+
+#define MAGICSTR "nakit ja muusiperunat maustevoilla"
+static void *
+dofifow(void *arg)
+{
+ int fd = (int)(uintptr_t)arg;
+ char buf[512];
+
+ printf("writing\n");
+ strcpy(buf, MAGICSTR);
+ if (rump_sys_write(fd, buf, strlen(buf)+1) != strlen(buf)+1)
+ atf_tc_fail_errno("write to fifo");
+
+ return NULL;
+}
+
+ATF_TC_BODY(root_fifo, tc)
+{
+ MAKEOPTS("-r", "fifo");
+ void *args;
+ pthread_t pt;
+ char buf[512];
+ int fd;
+
+ FSTEST_CONSTRUCTOR_FSPRIV(tc, puffs, args, theopts);
+
+ fd = rump_sys_open(FSTEST_MNTNAME, O_RDWR);
+ if (fd == -1)
+ atf_tc_fail_errno("open fifo");
+
+ pthread_create(&pt, NULL, dofifow, (void *)(uintptr_t)fd);
+
+ memset(buf, 0, sizeof(buf));
+ if (rump_sys_read(fd, buf, sizeof(buf)) == -1)
+ atf_tc_fail_errno("read fifo");
+
+ ATF_REQUIRE_STREQ(buf, MAGICSTR);
+ rump_sys_close(fd);
+
+ FSTEST_DESTRUCTOR(tc, puffs, args);
+}
+
+ATF_TC(root_chrdev);
+ATF_TC_HEAD(root_chrdev, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "root is /dev/null");
+}
+
+ATF_TC_BODY(root_chrdev, tc)
+{
+ MAKEOPTS("-r", "chr 2 2");
+ void *args;
+ ssize_t rv;
+ char buf[512];
+ int fd;
+
+ FSTEST_CONSTRUCTOR_FSPRIV(tc, puffs, args, theopts);
+
+ fd = rump_sys_open(FSTEST_MNTNAME, O_RDWR);
+ if (fd == -1)
+ atf_tc_fail_errno("open null");
+
+ rv = rump_sys_write(fd, buf, sizeof(buf));
+ ATF_REQUIRE(rv == sizeof(buf));
+
+ rv = rump_sys_read(fd, buf, sizeof(buf));
+ ATF_REQUIRE(rv == 0);
+
+ rump_sys_close(fd);
+
+ FSTEST_DESTRUCTOR(tc, puffs, args);
+}
+
+/*
+ * Inactive/reclaim tests
+ */
+
+ATF_TC(inactive_basic);
+ATF_TC_HEAD(inactive_basic, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "inactive gets called");
+}
+
+ATF_TC_BODY(inactive_basic, tc)
+{
+ struct puffstestargs *pargs;
+ void *args;
+ int fd;
+
+ FSTEST_CONSTRUCTOR(tc, puffs, args);
+ FSTEST_ENTER();
+ pargs = args;
+
+ fd = rump_sys_open("file", O_CREAT | O_RDWR, 0777);
+ if (fd == -1)
+ atf_tc_fail_errno("create");
+
+ /* none yet */
+ ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 0);
+
+ rump_sys_close(fd);
+
+ /* one for file */
+ ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 1);
+
+ FSTEST_EXIT();
+
+ /* another for the mountpoint */
+ ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 2);
+
+ FSTEST_DESTRUCTOR(tc, puffs, args);
+}
+
+ATF_TC(inactive_reclaim);
+ATF_TC_HEAD(inactive_reclaim, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "inactive/reclaim gets called");
+}
+
+ATF_TC_BODY(inactive_reclaim, tc)
+{
+ struct puffstestargs *pargs;
+ void *args;
+ int fd;
+
+ FSTEST_CONSTRUCTOR(tc, puffs, args);
+ FSTEST_ENTER();
+ pargs = args;
+
+ fd = rump_sys_open("file", O_CREAT | O_RDWR, 0777);
+ if (fd == -1)
+ atf_tc_fail_errno("create");
+
+ ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 0);
+
+ if (rump_sys_unlink("file") == -1)
+ atf_tc_fail_errno("remove");
+
+ ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 0);
+ ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 0);
+
+ rump_sys_close(fd);
+ syncbar(FSTEST_MNTNAME);
+
+ ATF_REQUIRE(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE] > 0);
+ ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 1);
+
+ FSTEST_EXIT();
+ FSTEST_DESTRUCTOR(tc, puffs, args);
+}
+
+ATF_TC(reclaim_hardlink);
+ATF_TC_HEAD(reclaim_hardlink, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "reclaim gets called only after "
+ "final link is gone");
+}
+
+ATF_TC_BODY(reclaim_hardlink, tc)
+{
+ struct puffstestargs *pargs;
+ void *args;
+ int fd;
+ int ianow;
+
+ FSTEST_CONSTRUCTOR(tc, puffs, args);
+ FSTEST_ENTER();
+ pargs = args;
+
+ fd = rump_sys_open("file", O_CREAT | O_RDWR, 0777);
+ if (fd == -1)
+ atf_tc_fail_errno("create");
+
+ if (rump_sys_link("file", "anotherfile") == -1)
+ atf_tc_fail_errno("create link");
+ rump_sys_close(fd);
+
+ ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 0);
+
+ /* unlink first hardlink */
+ if (rump_sys_unlink("file") == -1)
+ atf_tc_fail_errno("unlink 1");
+
+ ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 0);
+ ianow = pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE];
+
+ /* unlink second hardlink */
+ if (rump_sys_unlink("anotherfile") == -1)
+ atf_tc_fail_errno("unlink 2");
+
+ syncbar(FSTEST_MNTNAME);
+
+ ATF_REQUIRE(ianow < pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE]);
+ ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 1);
+
+ FSTEST_EXIT();
+ FSTEST_DESTRUCTOR(tc, puffs, args);
+}
+
+ATF_TC(unlink_accessible);
+ATF_TC_HEAD(unlink_accessible, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "open file is accessible after "
+ "having been unlinked");
+}
+
+ATF_TC_BODY(unlink_accessible, tc)
+{
+ MAKEOPTS("-i", "-o", "nopagecache");
+ struct puffstestargs *pargs;
+ void *args;
+ char buf[512];
+ int fd, ianow;
+
+ assert(sizeof(buf) > sizeof(MAGICSTR));
+
+ FSTEST_CONSTRUCTOR_FSPRIV(tc, puffs, args, theopts);
+ FSTEST_ENTER();
+ pargs = args;
+
+ fd = rump_sys_open("file", O_CREAT | O_RDWR, 0777);
+ if (fd == -1)
+ atf_tc_fail_errno("create");
+
+ if (rump_sys_write(fd, MAGICSTR, sizeof(MAGICSTR)) != sizeof(MAGICSTR))
+ atf_tc_fail_errno("write");
+ if (rump_sys_unlink("file") == -1)
+ atf_tc_fail_errno("unlink");
+
+ ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 0);
+ ianow = pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE];
+
+ if (rump_sys_pread(fd, buf, sizeof(buf), 0) == -1)
+ atf_tc_fail_errno("read");
+ rump_sys_close(fd);
+
+ syncbar(FSTEST_MNTNAME);
+
+ ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 1);
+ ATF_REQUIRE(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE] > ianow);
+
+ ATF_REQUIRE_STREQ(buf, MAGICSTR);
+
+ FSTEST_EXIT();
+ FSTEST_DESTRUCTOR(tc, puffs, args);
+}
+
+ATF_TC(signals);
+ATF_TC_HEAD(signals, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "Checks that sending a signal can "
+ "cause an interrupt to puffs wait");
+}
+
+extern struct proc *rumpns_initproc;
+extern void rumpns_psignal(struct proc *, int);
+extern void rumpns_sigclearall(struct proc *, void *, void *);
+ATF_TC_BODY(signals, tc)
+{
+ struct stat sb;
+ void *args;
+
+ rump_boot_setsigmodel(RUMP_SIGMODEL_RECORD);
+
+ FSTEST_CONSTRUCTOR(tc, puffs, args);
+ FSTEST_ENTER();
+ RL(rump_sys_stat(".", &sb));
+
+ /* send SIGUSR1, should not affect puffs ops */
+ rump_schedule();
+ rumpns_psignal(rumpns_initproc, SIGUSR1);
+ rump_unschedule();
+ RL(rump_sys_stat(".", &sb));
+
+ /* send SIGTERM, should get EINTR */
+ rump_schedule();
+ rumpns_psignal(rumpns_initproc, SIGTERM);
+ rump_unschedule();
+ ATF_REQUIRE_ERRNO(EINTR, rump_sys_stat(".", &sb) == -1);
+
+ /* clear sigmask so that we can unmount */
+ rump_schedule();
+ rumpns_sigclearall(rumpns_initproc, NULL, NULL);
+ rump_unschedule();
+
+ FSTEST_EXIT();
+ FSTEST_DESTRUCTOR(tc, puffs, args);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, mount);
+
+ ATF_TP_ADD_TC(tp, root_fifo);
+ ATF_TP_ADD_TC(tp, root_lnk);
+ ATF_TP_ADD_TC(tp, root_reg);
+ ATF_TP_ADD_TC(tp, root_chrdev);
+
+ ATF_TP_ADD_TC(tp, inactive_basic);
+ ATF_TP_ADD_TC(tp, inactive_reclaim);
+ ATF_TP_ADD_TC(tp, reclaim_hardlink);
+ ATF_TP_ADD_TC(tp, unlink_accessible);
+
+ ATF_TP_ADD_TC(tp, signals);
+
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/puffs/t_fuzz.c b/contrib/netbsd-tests/fs/puffs/t_fuzz.c
new file mode 100644
index 000000000000..b2e000d07e5c
--- /dev/null
+++ b/contrib/netbsd-tests/fs/puffs/t_fuzz.c
@@ -0,0 +1,283 @@
+/* $NetBSD: t_fuzz.c,v 1.6 2017/01/13 21:30:40 christos Exp $ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Fuzztest puffs mount. There are n different levels of testing:
+ * each one pours more and more sane garbage into the args to that
+ * the mount progresses further and further. Level 8 (at least when
+ * writing this comment) should be the one where mounting actually
+ * succeeds.
+ *
+ * Our metric of success is crash / no crash.
+ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/poll.h>
+
+#include <assert.h>
+#include <atf-c.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <fs/puffs/puffs_msgif.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_macros.h"
+
+#define ITERATIONS 100
+
+static void
+fixversion(struct puffs_kargs *kargs)
+{
+
+ kargs->pa_vers = PUFFSVERSION;
+}
+
+static void
+fixkflag(struct puffs_kargs *kargs)
+{
+
+ kargs->pa_flags &= PUFFS_KFLAG_MASK;
+
+ /*
+ * PUFFS_KFLAG_CACHE_FS_TTL require extended behavior
+ * from the filesystem for which we have no test right now.
+ */
+ kargs->pa_flags &= ~PUFFS_KFLAG_CACHE_FS_TTL;
+}
+
+static void
+fixfhflag(struct puffs_kargs *kargs)
+{
+
+ kargs->pa_fhflags &= PUFFS_FHFLAG_MASK;
+}
+
+static void
+fixspare(struct puffs_kargs *kargs)
+{
+
+ memset(&kargs->pa_spare, 0, sizeof(kargs->pa_spare));
+}
+
+static void
+fixhandsize(struct puffs_kargs *kargs)
+{
+
+ kargs->pa_fhsize %= PUFFS_FHSIZE_MAX+4;
+}
+
+static void
+fixhandsize2(struct puffs_kargs *kargs)
+{
+
+ /* XXX: values */
+ if (kargs->pa_fhflags & PUFFS_FHFLAG_NFSV3)
+ kargs->pa_fhsize %= 60;
+ if (kargs->pa_fhflags & PUFFS_FHFLAG_NFSV2)
+ kargs->pa_fhsize %= 28;
+}
+
+static void
+fixputter(struct puffs_kargs *kargs)
+{
+
+ kargs->pa_fd = rump_sys_open("/dev/putter", O_RDWR);
+ if (kargs->pa_fd == -1)
+ atf_tc_fail_errno("open putter");
+}
+
+static void
+fixroot(struct puffs_kargs *kargs)
+{
+
+ kargs->pa_root_vtype %= VBAD;
+}
+
+static void
+unfixputter(struct puffs_kargs *kargs)
+{
+
+ rump_sys_close(kargs->pa_fd);
+}
+
+typedef void (*fixfn)(struct puffs_kargs *);
+static fixfn fixstack[] = {
+ fixversion,
+ fixkflag,
+ fixfhflag,
+ fixspare,
+ fixhandsize,
+ fixhandsize2,
+ fixputter,
+ fixroot,
+};
+
+static void
+fixup(int nfix, struct puffs_kargs *kargs)
+{
+ int i;
+
+ assert(nfix <= __arraycount(fixstack));
+ for (i = 0; i < nfix; i++)
+ fixstack[i](kargs);
+}
+
+static void
+unfixup(int nfix, struct puffs_kargs *kargs)
+{
+
+ if (nfix >= 7)
+ unfixputter(kargs);
+}
+
+static pthread_mutex_t damtx;
+static pthread_cond_t dacv;
+static int dafd = -1;
+
+static void *
+respondthread(void *arg)
+{
+ char buf[PUFFS_MSG_MAXSIZE];
+ struct puffs_req *preq = (void *)buf;
+ struct pollfd pfd;
+ ssize_t n;
+
+ pthread_mutex_lock(&damtx);
+ for (;;) {
+ while (dafd == -1)
+ pthread_cond_wait(&dacv, &damtx);
+
+ while (dafd != -1) {
+ pthread_mutex_unlock(&damtx);
+ pfd.fd = dafd;
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+ if (rump_sys_poll(&pfd, 1, 10) == 0) {
+ pthread_mutex_lock(&damtx);
+ continue;
+ }
+ n = rump_sys_read(dafd, buf, sizeof(buf));
+ if (n <= 0) {
+ pthread_mutex_lock(&damtx);
+ break;
+ }
+
+ /* just say it was succesful */
+ preq->preq_rv = 0;
+ rump_sys_write(dafd, buf, n);
+ pthread_mutex_lock(&damtx);
+ }
+ }
+
+ return NULL;
+}
+
+static void
+testbody(int nfix)
+{
+ pthread_t pt;
+ struct puffs_kargs kargs;
+ unsigned long seed;
+ int i;
+
+ seed = time(NULL);
+ srandom(seed);
+ printf("test seeded RNG with %lu\n", seed);
+
+ rump_init();
+
+ pthread_mutex_init(&damtx, NULL);
+ pthread_cond_init(&dacv, NULL);
+ pthread_create(&pt, NULL, respondthread, NULL);
+
+ ATF_REQUIRE(rump_sys_mkdir("/mnt", 0777) == 0);
+
+ for (i = 0; i < ITERATIONS; i++) {
+ tests_makegarbage(&kargs, sizeof(kargs));
+ fixup(nfix, &kargs);
+ if (rump_sys_mount(MOUNT_PUFFS, "/mnt", 0,
+ &kargs, sizeof(kargs)) == 0) {
+ struct stat sb;
+
+ pthread_mutex_lock(&damtx);
+ dafd = kargs.pa_fd;
+ pthread_cond_signal(&dacv);
+ pthread_mutex_unlock(&damtx);
+
+ rump_sys_stat("/mnt", &sb);
+ rump_sys_unmount("/mnt", MNT_FORCE);
+ }
+ unfixup(nfix, &kargs);
+
+ pthread_mutex_lock(&damtx);
+ dafd = -1;
+ pthread_mutex_unlock(&damtx);
+ }
+}
+
+#define MAKETEST(_n_) \
+ATF_TC(mountfuzz##_n_); \
+ATF_TC_HEAD(mountfuzz##_n_, tc) \
+{atf_tc_set_md_var(tc, "descr", "garbage kargs, " # _n_ " fix(es)");} \
+ATF_TC_BODY(mountfuzz##_n_, tc) {testbody(_n_);}
+
+MAKETEST(0);
+MAKETEST(1);
+MAKETEST(2);
+MAKETEST(3);
+MAKETEST(4);
+MAKETEST(5);
+MAKETEST(6);
+MAKETEST(7);
+MAKETEST(8);
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, mountfuzz0);
+ ATF_TP_ADD_TC(tp, mountfuzz1);
+ ATF_TP_ADD_TC(tp, mountfuzz2);
+ ATF_TP_ADD_TC(tp, mountfuzz3);
+ ATF_TP_ADD_TC(tp, mountfuzz4);
+ ATF_TP_ADD_TC(tp, mountfuzz5);
+ ATF_TP_ADD_TC(tp, mountfuzz6);
+ ATF_TP_ADD_TC(tp, mountfuzz7);
+ ATF_TP_ADD_TC(tp, mountfuzz8);
+
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/puffs/t_io.c b/contrib/netbsd-tests/fs/puffs/t_io.c
new file mode 100644
index 000000000000..72b5d85e23ff
--- /dev/null
+++ b/contrib/netbsd-tests/fs/puffs/t_io.c
@@ -0,0 +1,61 @@
+/* $NetBSD: t_io.c,v 1.2 2017/01/13 21:30:40 christos Exp $ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+
+#include <assert.h>
+#include <atf-c.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <puffs.h>
+#include <puffsdump.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_macros.h"
+#include "../common/h_fsmacros.h"
+
+#define MAKEOPTS(...) \
+ char *theopts[] = {NULL, "-s", __VA_ARGS__, "dtfs", "n/a", NULL}
+
+ATF_TC(nocache);
+ATF_TC_HEAD(nocache, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "tests large i/o without page cache");
+}
+
+ATF_TC_BODY(nocache, tc)
+{
+ MAKEOPTS("-o", "nopagecache");
+ char data[1024*1024];
+ void *args;
+ int fd;
+
+ FSTEST_CONSTRUCTOR_FSPRIV(tc, puffs, args, theopts);
+ FSTEST_ENTER();
+
+ RL(fd = rump_sys_open("afile", O_CREAT | O_RDWR, 0755));
+ RL(rump_sys_write(fd, data, sizeof(data)));
+ rump_sys_close(fd);
+
+ FSTEST_EXIT();
+ FSTEST_DESTRUCTOR(tc, puffs, args);
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, nocache);
+
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/README b/contrib/netbsd-tests/fs/tmpfs/README
new file mode 100644
index 000000000000..cdc3f2d11ca7
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/README
@@ -0,0 +1,17 @@
+The tests in this directory where written at the same time tmpfs was
+developed. This is why, if you follow the order of tests in the Atffile,
+you will notice that they start checking the most basic things and end
+checking the less common ones. Furthermore, tests try not to use features
+tested by further tests in the lists.
+
+However, the above is not the most appropriate testing procedure when you
+have a working file system because some separation in test programs does
+not make sense afterwards.
+
+Many of the tests here are applicable to any file system. They should be
+refactored to be reusable on any mounted file system, which could also
+remove the need to do the mount/unmount steps in each and every test case.
+
+Possibly take a look at the file system tests in FreeBSD. They seem to be
+much more complete, even though they are written in Perl and therefore not
+directly usable.
diff --git a/contrib/netbsd-tests/fs/tmpfs/h_funcs.subr b/contrib/netbsd-tests/fs/tmpfs/h_funcs.subr
new file mode 100644
index 000000000000..edab7899ea71
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/h_funcs.subr
@@ -0,0 +1,115 @@
+#!/bin/sh
+#
+# $NetBSD: h_funcs.subr,v 1.5 2013/03/17 01:16:45 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+Mount_Point=
+
+#
+# test_mount [args]
+#
+# Mounts tmpfs over ${Mount_Point} and changes the current directory
+# to the mount point. Optional arguments may be passed to the
+# mount command.
+#
+test_mount() {
+ require_fs tmpfs
+
+ Mount_Point=$(pwd)/mntpt
+ atf_check -s eq:0 -o empty -e empty mkdir "${Mount_Point}"
+ echo "mount -t tmpfs ${*} tmpfs ${Mount_Point}"
+ mount -t tmpfs "${@}" tmpfs "${Mount_Point}" 2>mounterr
+ if [ "${?}" -ne 0 ]; then
+ cat mounterr 1>&2
+ if grep 'Operation not supported' mounterr > /dev/null; then
+ atf_skip "tmpfs not supported"
+ fi
+ atf_fail "Failed to mount a tmpfs file system"
+ fi
+ cd "${Mount_Point}"
+}
+
+#
+# test_unmount
+#
+# Unmounts the file system mounted by test_mount.
+#
+test_unmount() {
+ # Begin FreeBSD
+ _test_unmount
+ exit_code=$?
+ atf_check_equal "$exit_code" "0"
+ return $exit_code
+ # End FreeBSD
+ cd - >/dev/null
+ atf_check -s eq:0 -o empty -e empty umount ${Mount_Point}
+ atf_check -s eq:0 -o empty -e empty rmdir ${Mount_Point}
+ Mount_Point=
+}
+
+# Begin FreeBSD
+_test_unmount() {
+ if [ -z "${Mount_Point}" -o ! -d "${Mount_Point}" ]; then
+ return 0
+ fi
+
+ cd - >/dev/null
+ umount ${Mount_Point}
+ rmdir ${Mount_Point}
+ Mount_Point=
+}
+# End FreeBSD
+
+#
+# kqueue_monitor expected_nevents file1 [.. fileN]
+#
+# Monitors the commands given through stdin (one per line) using
+# kqueue and stores the events raised in a log that can be later
+# verified with kqueue_check.
+#
+kqueue_monitor() {
+ nev=${1}; shift
+ echo "Running kqueue-monitored commands and expecting" \
+ "${nev} events"
+ $(atf_get_srcdir)/h_tools kqueue ${*} >kqueue.log || \
+ atf_fail "Could not launch kqueue monitor"
+ got=$(wc -l kqueue.log | awk '{ print $1 }')
+ test ${got} -eq ${nev} || \
+ atf_fail "Got ${got} events but expected ${nev}"
+}
+
+#
+# kqueue_check file event
+#
+# Checks if kqueue raised the given event when monitoring the
+# given file.
+#
+kqueue_check() {
+ echo "Checking if ${1} received ${2}"
+ grep "^${1} - ${2}$" kqueue.log >/dev/null || \
+ atf_fail "${1} did not receive ${2}"
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/h_tools.c b/contrib/netbsd-tests/fs/tmpfs/h_tools.c
new file mode 100644
index 000000000000..492e084d2a5c
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/h_tools.c
@@ -0,0 +1,321 @@
+/* $NetBSD: h_tools.c,v 1.4 2011/06/11 18:03:17 christos Exp $ */
+
+/*
+ * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Helper tools for several tests. These are kept in a single file due
+ * to the limitations of bsd.prog.mk to build a single program in a
+ * given directory.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/mount.h>
+#include <sys/statvfs.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/un.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __FreeBSD__
+#include <inttypes.h>
+#endif
+
+/* --------------------------------------------------------------------- */
+
+static int getfh_main(int, char **);
+static int kqueue_main(int, char **);
+static int rename_main(int, char **);
+static int sockets_main(int, char **);
+static int statvfs_main(int, char **);
+
+/* --------------------------------------------------------------------- */
+
+int
+getfh_main(int argc, char **argv)
+{
+ int error;
+ void *fh;
+ size_t fh_size;
+
+ if (argc < 2)
+ return EXIT_FAILURE;
+
+#ifdef __FreeBSD__
+ fh_size = sizeof(fhandle_t);
+#else
+ fh_size = 0;
+#endif
+
+ fh = NULL;
+ for (;;) {
+ if (fh_size) {
+ fh = malloc(fh_size);
+ if (fh == NULL) {
+ fprintf(stderr, "out of memory");
+ return EXIT_FAILURE;
+ }
+ }
+ /*
+ * The kernel provides the necessary size in fh_size -
+ * but it may change if someone moves things around,
+ * so retry untill we have enough memory.
+ */
+#ifdef __FreeBSD__
+ error = getfh(argv[1], fh);
+#else
+ error = getfh(argv[1], fh, &fh_size);
+#endif
+ if (error == 0) {
+ break;
+ } else {
+ if (fh != NULL)
+ free(fh);
+ if (errno != E2BIG) {
+ warn("getfh");
+ return EXIT_FAILURE;
+ }
+ }
+ }
+
+ error = write(STDOUT_FILENO, fh, fh_size);
+ if (error == -1) {
+ warn("write");
+ return EXIT_FAILURE;
+ }
+ free(fh);
+
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+kqueue_main(int argc, char **argv)
+{
+ char *line;
+ int i, kq;
+ size_t len;
+ struct kevent *changes, event;
+
+ if (argc < 2)
+ return EXIT_FAILURE;
+
+ argc--;
+ argv++;
+
+ changes = malloc(sizeof(struct kevent) * argc);
+ if (changes == NULL)
+ errx(EXIT_FAILURE, "not enough memory");
+
+ for (i = 0; i < argc; i++) {
+ int fd;
+
+ fd = open(argv[i], O_RDONLY);
+ if (fd == -1)
+ err(EXIT_FAILURE, "cannot open %s", argv[i]);
+
+ EV_SET(&changes[i], fd, EVFILT_VNODE,
+ EV_ADD | EV_ENABLE | EV_ONESHOT,
+ NOTE_ATTRIB | NOTE_DELETE | NOTE_EXTEND | NOTE_LINK |
+ NOTE_RENAME | NOTE_REVOKE | NOTE_WRITE,
+ 0, 0);
+ }
+
+ kq = kqueue();
+ if (kq == -1)
+ err(EXIT_FAILURE, "kqueue");
+
+ while ((line = fgetln(stdin, &len)) != NULL) {
+ int ec, nev;
+ struct timespec to;
+
+ to.tv_sec = 0;
+ to.tv_nsec = 100000;
+
+ (void)kevent(kq, changes, argc, &event, 1, &to);
+
+ assert(len > 0);
+ assert(line[len - 1] == '\n');
+ line[len - 1] = '\0';
+ ec = system(line);
+ if (ec != EXIT_SUCCESS)
+ errx(ec, "%s returned %d", line, ec);
+
+ do {
+ nev = kevent(kq, changes, argc, &event, 1, &to);
+ if (nev == -1)
+ err(EXIT_FAILURE, "kevent");
+ else if (nev > 0) {
+ for (i = 0; i < argc; i++)
+ if (event.ident == changes[i].ident)
+ break;
+
+ if (event.fflags & NOTE_ATTRIB)
+ printf("%s - NOTE_ATTRIB\n", argv[i]);
+ if (event.fflags & NOTE_DELETE)
+ printf("%s - NOTE_DELETE\n", argv[i]);
+ if (event.fflags & NOTE_EXTEND)
+ printf("%s - NOTE_EXTEND\n", argv[i]);
+ if (event.fflags & NOTE_LINK)
+ printf("%s - NOTE_LINK\n", argv[i]);
+ if (event.fflags & NOTE_RENAME)
+ printf("%s - NOTE_RENAME\n", argv[i]);
+ if (event.fflags & NOTE_REVOKE)
+ printf("%s - NOTE_REVOKE\n", argv[i]);
+ if (event.fflags & NOTE_WRITE)
+ printf("%s - NOTE_WRITE\n", argv[i]);
+ }
+ } while (nev > 0);
+ }
+
+ for (i = 0; i < argc; i++)
+ close(changes[i].ident);
+ free(changes);
+
+ return EXIT_SUCCESS;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+rename_main(int argc, char **argv)
+{
+
+ if (argc < 3)
+ return EXIT_FAILURE;
+
+ if (rename(argv[1], argv[2]) == -1) {
+ warn("rename");
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+sockets_main(int argc, char **argv)
+{
+ int error, fd;
+ struct sockaddr_un addr;
+
+ if (argc < 2)
+ return EXIT_FAILURE;
+
+ fd = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (fd == -1) {
+ warn("socket");
+ return EXIT_FAILURE;
+ }
+
+#ifdef __FreeBSD__
+ memset(&addr, 0, sizeof(addr));
+#endif
+ (void)strlcpy(addr.sun_path, argv[1], sizeof(addr.sun_path));
+ addr.sun_family = PF_UNIX;
+#ifdef __FreeBSD__
+ error = bind(fd, (struct sockaddr *)&addr, SUN_LEN(&addr));
+#else
+ error = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
+#endif
+ if (error == -1) {
+ warn("connect");
+#ifdef __FreeBSD__
+ (void)close(fd);
+#endif
+ return EXIT_FAILURE;
+ }
+
+ close(fd);
+
+ return EXIT_SUCCESS;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+statvfs_main(int argc, char **argv)
+{
+ int error;
+ struct statvfs buf;
+
+ if (argc < 2)
+ return EXIT_FAILURE;
+
+ error = statvfs(argv[1], &buf);
+ if (error != 0) {
+ warn("statvfs");
+ return EXIT_FAILURE;
+ }
+
+ (void)printf("f_bsize=%lu\n", buf.f_bsize);
+ (void)printf("f_blocks=%" PRId64 "\n", buf.f_blocks);
+ (void)printf("f_bfree=%" PRId64 "\n", buf.f_bfree);
+ (void)printf("f_files=%" PRId64 "\n", buf.f_files);
+
+ return EXIT_SUCCESS;
+}
+
+/* --------------------------------------------------------------------- */
+
+int
+main(int argc, char **argv)
+{
+ int error;
+
+ if (argc < 2)
+ return EXIT_FAILURE;
+
+ argc -= 1;
+ argv += 1;
+
+ if (strcmp(argv[0], "getfh") == 0)
+ error = getfh_main(argc, argv);
+ else if (strcmp(argv[0], "kqueue") == 0)
+ error = kqueue_main(argc, argv);
+ else if (strcmp(argv[0], "rename") == 0)
+ error = rename_main(argc, argv);
+ else if (strcmp(argv[0], "sockets") == 0)
+ error = sockets_main(argc, argv);
+ else if (strcmp(argv[0], "statvfs") == 0)
+ error = statvfs_main(argc, argv);
+ else
+ error = EXIT_FAILURE;
+
+ return error;
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_create.sh b/contrib/netbsd-tests/fs/tmpfs/t_create.sh
new file mode 100755
index 000000000000..f1f894dfd65f
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_create.sh
@@ -0,0 +1,122 @@
+# $NetBSD: t_create.sh,v 1.8 2011/03/05 07:41:11 pooka Exp $
+#
+# Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# Verifies that the create operation works.
+#
+
+atf_test_case create
+create_head() {
+ atf_set "descr" "Verifies that files can be created"
+ atf_set "require.user" "root"
+}
+create_body() {
+ test_mount
+
+ atf_check -s eq:1 -o empty -e empty test -f a
+ atf_check -s eq:0 -o empty -e empty touch a
+ atf_check -s eq:0 -o empty -e empty test -f a
+
+ test_unmount
+}
+
+atf_test_case attrs
+attrs_head() {
+ atf_set "descr" "Verifies that a new file gets the correct" \
+ "attributes"
+ atf_set "require.config" "unprivileged-user"
+ atf_set "require.user" "root"
+}
+attrs_body() {
+ user=$(atf_config_get unprivileged-user)
+ # Allow the unprivileged user to access the work directory.
+ chown ${user} .
+
+ test_mount
+
+ umask 022
+ atf_check -s eq:1 -o empty -e empty test -f a
+ atf_check -s eq:0 -o empty -e empty touch a
+ atf_check -s eq:0 -o empty -e empty test -f a
+
+ eval $(stat -s . | sed -e 's|st_|dst_|g')
+ eval $(stat -s a)
+ test ${st_flags} -eq 0 || atf_fail "Incorrect flags"
+ test ${st_size} -eq 0 || atf_fail "Incorrect size"
+ test ${st_uid} -eq $(id -u) || atf_fail "Incorrect uid"
+ test ${st_gid} -eq ${dst_gid} || atf_fail "Incorrect gid"
+ test ${st_mode} = 0100644 || atf_fail "Incorrect mode"
+
+ atf_check -s eq:0 -o empty -e empty mkdir b c
+
+ atf_check -s eq:0 -o empty -e empty chown ${user}:0 b
+ eval $(stat -s b)
+ [ ${st_uid} -eq $(id -u ${user}) ] || atf_fail "Incorrect owner"
+ [ ${st_gid} -eq 0 ] || atf_fail "Incorrect group"
+
+ atf_check -s eq:0 -o empty -e empty chown ${user}:100 c
+ eval $(stat -s c)
+ [ ${st_uid} -eq $(id -u ${user}) ] || atf_fail "Incorrect owner"
+ [ ${st_gid} -eq 100 ] || atf_fail "Incorrect group"
+
+ atf_check -s eq:0 -o empty -e empty su -m ${user} -c 'touch b/a'
+ eval $(stat -s b/a)
+ [ ${st_uid} -eq $(id -u ${user}) ] || atf_fail "Incorrect owner"
+ [ ${st_gid} -eq 0 ] || atf_fail "Incorrect group"
+
+ atf_check -s eq:0 -o empty -e empty su -m ${user} -c 'touch c/a'
+ eval $(stat -s c/a)
+ [ ${st_uid} -eq $(id -u ${user}) ] || atf_fail "Incorrect owner"
+ [ ${st_gid} -eq 100 ] || atf_fail "Incorrect group"
+
+ test_unmount
+}
+
+atf_test_case kqueue
+kqueue_head() {
+ atf_set "descr" "Verifies that creating a file raises the correct" \
+ "kqueue events"
+ atf_set "require.user" "root"
+}
+kqueue_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir dir
+ echo 'touch dir/a' | kqueue_monitor 1 dir
+ kqueue_check dir NOTE_WRITE
+
+ test_unmount
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case create
+ atf_add_test_case attrs
+ atf_add_test_case kqueue
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_devices.sh b/contrib/netbsd-tests/fs/tmpfs/t_devices.sh
new file mode 100755
index 000000000000..472d37860dd8
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_devices.sh
@@ -0,0 +1,60 @@
+# $NetBSD: t_devices.sh,v 1.5 2010/11/07 17:51:18 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+atf_test_case basic
+basic_head() {
+ atf_set "descr" "Tests that special devices work"
+ atf_set "require.user" "root"
+}
+basic_body() {
+ test_mount
+
+ umask 022
+
+ atf_check -s eq:0 -o ignore -e ignore /dev/MAKEDEV std
+ atf_check -s eq:0 -o empty -e empty test -e zero
+ atf_check -s eq:0 -o empty -e empty test -e null
+
+ echo "Reading from the 'zero' character device"
+ atf_check -s eq:0 -o ignore -e ignore dd if=zero of=a bs=10k count=1
+ [ $(md5 a | cut -d ' ' -f 4) = 1276481102f218c981e0324180bafd9f ] || \
+ atf_fail "Data read is invalid"
+
+ echo "Writing to the 'null' device"
+ echo foo >null
+ eval $(stat -s null)
+ [ ${st_size} -eq 0 ] || atf_fail "Invalid size for device"
+
+ test_unmount
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case basic
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_dots.sh b/contrib/netbsd-tests/fs/tmpfs/t_dots.sh
new file mode 100755
index 000000000000..e480de7658ac
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_dots.sh
@@ -0,0 +1,67 @@
+# $NetBSD: t_dots.sh,v 1.5 2010/11/07 17:51:18 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+atf_test_case topdir
+topdir_head() {
+ atf_set "descr" "Verifies that looking up '.' and '..' in" \
+ "top-level directories works"
+ atf_set "require.user" "root"
+}
+topdir_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir a
+ atf_check -s eq:0 -o empty -e empty test -d ./a
+ atf_check -s eq:0 -o empty -e empty test -d a/../a
+
+ test_unmount
+}
+
+atf_test_case nesteddir
+nesteddir_head() {
+ atf_set "descr" "Verifies that looking up '.' and '..' in" \
+ "top-level directories works"
+ atf_set "require.user" "root"
+}
+nesteddir_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir a
+ atf_check -s eq:0 -o empty -e empty mkdir a/b
+ atf_check -s eq:0 -o empty -e empty test -d a/b/../b
+ atf_check -s eq:0 -o empty -e empty test -d a/b/../../a
+
+ test_unmount
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case topdir
+ atf_add_test_case nesteddir
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_exec.sh b/contrib/netbsd-tests/fs/tmpfs/t_exec.sh
new file mode 100755
index 000000000000..9ffc6008a139
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_exec.sh
@@ -0,0 +1,52 @@
+# $NetBSD: t_exec.sh,v 1.5 2010/11/07 17:51:18 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+atf_test_case basic
+basic_head() {
+ atf_set "descr" "Verifies that binary files can be executed from" \
+ "within the file system (i.e., whether getpages" \
+ "works)"
+ atf_set "require.user" "root"
+}
+basic_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty cp /bin/cp .
+ echo "Verifying copied file"
+ [ $(md5 cp | cut -d ' ' -f 4) = $(md5 /bin/cp | cut -d ' ' -f 4) ] || \
+ atf_file "New binary file does not match original"
+ atf_check -s eq:0 -o empty -e empty ./cp cp foo
+
+ test_unmount
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case basic
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_link.sh b/contrib/netbsd-tests/fs/tmpfs/t_link.sh
new file mode 100755
index 000000000000..612c1e204509
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_link.sh
@@ -0,0 +1,144 @@
+# $NetBSD: t_link.sh,v 1.5 2010/11/07 17:51:18 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# Verifies that the link operation works.
+#
+
+atf_test_case basic
+basic_head() {
+ atf_set "descr" "Verifies that the link operation works on files" \
+ "at the top directory"
+ atf_set "require.user" "root"
+}
+basic_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty touch a
+ atf_check -s eq:0 -o empty -e empty touch z
+ eval $(stat -s a | sed -e 's|st_|sta_|g')
+ eval $(stat -s z | sed -e 's|st_|stz_|g')
+ test ${sta_ino} != ${stz_ino} || \
+ atf_fail "Node numbers are not different"
+ test ${sta_nlink} -eq 1 || atf_fail "Number of links is incorrect"
+ atf_check -s eq:0 -o empty -e empty ln a b
+
+ echo "Checking if link count is correct after links are created"
+ eval $(stat -s a | sed -e 's|st_|sta_|g')
+ eval $(stat -s b | sed -e 's|st_|stb_|g')
+ test ${sta_ino} = ${stb_ino} || atf_fail "Node number changed"
+ test ${sta_nlink} -eq 2 || atf_fail "Link count is incorrect"
+ test ${stb_nlink} -eq 2 || atf_fail "Link count is incorrect"
+
+ echo "Checking if link count is correct after links are deleted"
+ atf_check -s eq:0 -o empty -e empty rm a
+ eval $(stat -s b | sed -e 's|st_|stb_|g')
+ test ${stb_nlink} -eq 1 || atf_fail "Link count is incorrect"
+ atf_check -s eq:0 -o empty -e empty rm b
+
+ test_unmount
+}
+
+atf_test_case subdirs
+subdirs_head() {
+ atf_set "descr" "Verifies that the link operation works if used" \
+ "in subdirectories"
+ atf_set "require.user" "root"
+}
+subdirs_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty touch a
+ atf_check -s eq:0 -o empty -e empty mkdir c
+ atf_check -s eq:0 -o empty -e empty ln a c/b
+
+ echo "Checking if link count is correct after links are created"
+ eval $(stat -s a | sed -e 's|st_|sta_|g')
+ eval $(stat -s c/b | sed -e 's|st_|stb_|g')
+ test ${sta_ino} = ${stb_ino} || atf_fail "Node number changed"
+ test ${sta_nlink} -eq 2 || atf_fail "Link count is incorrect"
+ test ${stb_nlink} -eq 2 || atf_fail "Link count is incorrect"
+
+ echo "Checking if link count is correct after links are deleted"
+ atf_check -s eq:0 -o empty -e empty rm a
+ eval $(stat -s c/b | sed -e 's|st_|stb_|g')
+ test ${stb_nlink} -eq 1 || atf_fail "Link count is incorrect"
+ atf_check -s eq:0 -o empty -e empty rm c/b
+ atf_check -s eq:0 -o empty -e empty rmdir c
+
+ test_unmount
+}
+
+# Begin FreeBSD
+if true; then
+atf_test_case kqueue cleanup
+kqueue_cleanup() {
+ Mount_Point=$(pwd)/mntpt _test_unmount || :
+}
+else
+# End FreeBSD
+atf_test_case kqueue
+# Begin FreeBSD
+fi
+# End FreeBSD
+kqueue_head() {
+ atf_set "descr" "Verifies that creating a link raises the correct" \
+ "kqueue events"
+ atf_set "require.user" "root"
+}
+kqueue_body() {
+ test_mount
+
+ # Begin FreeBSD
+ atf_expect_fail "fails with: dir/b did not receive NOTE_LINK - bug 213662"
+ # End FreeBSD
+
+ atf_check -s eq:0 -o empty -e empty mkdir dir
+ atf_check -s eq:0 -o empty -e empty touch dir/a
+ echo 'ln dir/a dir/b' | kqueue_monitor 2 dir dir/a
+ kqueue_check dir/a NOTE_LINK
+ kqueue_check dir NOTE_WRITE
+
+ echo 'rm dir/a' | kqueue_monitor 2 dir dir/b
+ # XXX According to the (short) kqueue(2) documentation, the following
+ # should raise a NOTE_LINK but FFS raises a NOTE_DELETE...
+ kqueue_check dir/b NOTE_LINK
+ kqueue_check dir NOTE_WRITE
+ atf_check -s eq:0 -o empty -e empty rm dir/b
+ atf_check -s eq:0 -o empty -e empty rmdir dir
+
+ test_unmount
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case basic
+ atf_add_test_case subdirs
+ atf_add_test_case kqueue
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_mkdir.sh b/contrib/netbsd-tests/fs/tmpfs/t_mkdir.sh
new file mode 100755
index 000000000000..db0d1e3c5c8f
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_mkdir.sh
@@ -0,0 +1,159 @@
+# $NetBSD: t_mkdir.sh,v 1.8 2011/03/05 07:41:11 pooka Exp $
+#
+# Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# Verifies that mkdir works by creating some nested directories. It also
+# checks, in part, the lookup operation.
+#
+
+atf_test_case single
+single_head() {
+ atf_set "descr" "Creates a single directory and checks the" \
+ "mount point's hard link count"
+ atf_set "require.user" "root"
+}
+single_body() {
+ test_mount
+
+ atf_check -s eq:1 -o empty -e empty test -d a
+ atf_check -s eq:0 -o empty -e empty mkdir a
+ atf_check -s eq:0 -o empty -e empty test -d a
+ test -d a
+ eval $(stat -s ${Mount_Point})
+ [ ${st_nlink} = 3 ] || atf_fail "Link count is not 3"
+
+ test_unmount
+}
+
+atf_test_case many
+many_head() {
+ atf_set "descr" "Creates multiple directories and checks the" \
+ "mount point's hard link count"
+ atf_set "require.user" "root"
+}
+many_body() {
+ test_mount
+
+ for d in $(jot 100); do
+ atf_check -s eq:1 -o empty -e empty test -d ${d}
+ atf_check -s eq:0 -o empty -e empty mkdir ${d}
+ atf_check -s eq:0 -o empty -e empty test -d ${d}
+ done
+ eval $(stat -s ${Mount_Point})
+ [ ${st_nlink} = 102 ] || atf_fail "Link count is not 102"
+
+ test_unmount
+}
+
+atf_test_case nested
+nested_head() {
+ atf_set "descr" "Checks if nested directories can be created"
+ atf_set "require.user" "root"
+}
+nested_body() {
+ test_mount
+
+ atf_check -s eq:1 -o empty -e empty test -d a/b/c/d/e
+ atf_check -s eq:0 -o empty -e empty mkdir -p a/b/c/d/e
+ atf_check -s eq:0 -o empty -e empty test -d a/b/c/d/e
+
+ test_unmount
+}
+
+atf_test_case attrs
+attrs_head() {
+ atf_set "descr" "Checks that new directories get the proper" \
+ "attributes (owner and group)"
+ atf_set "require.config" "unprivileged-user"
+ atf_set "require.user" "root"
+}
+attrs_body() {
+ user=$(atf_config_get unprivileged-user)
+ # Allow the unprivileged user to access the work directory.
+ chown ${user} .
+
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir b c
+
+ atf_check -s eq:0 -o empty -e empty chown ${user}:0 b
+ eval $(stat -s b)
+ [ ${st_uid} -eq $(id -u ${user}) ] || atf_fail "Incorrect owner"
+ [ ${st_gid} -eq 0 ] || atf_fail "Incorrect group"
+
+ atf_check -s eq:0 -o empty -e empty chown ${user}:100 c
+ eval $(stat -s c)
+ [ ${st_uid} -eq $(id -u ${user}) ] || atf_fail "Incorrect owner"
+ [ ${st_gid} -eq 100 ] || atf_fail "Incorrect group"
+
+ atf_check -s eq:0 -o empty -e empty su -m ${user} -c 'mkdir b/a'
+ eval $(stat -s b/a)
+ [ ${st_uid} -eq $(id -u ${user}) ] || atf_fail "Incorrect owner"
+ [ ${st_gid} -eq 0 ] || atf_fail "Incorrect group"
+
+ atf_check -s eq:0 -o empty -e empty su -m ${user} -c 'mkdir c/a'
+ eval $(stat -s c/a)
+ [ ${st_uid} -eq $(id -u ${user}) ] || atf_fail "Incorrect owner"
+ [ ${st_gid} -eq 100 ] || atf_fail "Incorrect group"
+
+ test_unmount
+}
+
+atf_test_case kqueue
+kqueue_head() {
+ atf_set "descr" "Creates a directory and checks the kqueue events" \
+ "raised"
+ atf_set "require.user" "root"
+}
+kqueue_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir dir
+ echo 'mkdir dir/a' | kqueue_monitor 2 dir
+
+ # Creating a directory raises NOTE_LINK on the parent directory
+ kqueue_check dir NOTE_LINK
+
+ # Creating a directory raises NOTE_WRITE on the parent directory
+ kqueue_check dir NOTE_WRITE
+
+ atf_check -s eq:0 -o empty -e empty rmdir dir/a
+ atf_check -s eq:0 -o empty -e empty rmdir dir
+
+ test_unmount
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case single
+ atf_add_test_case many
+ atf_add_test_case nested
+ atf_add_test_case attrs
+ atf_add_test_case kqueue
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_mknod.sh b/contrib/netbsd-tests/fs/tmpfs/t_mknod.sh
new file mode 100755
index 000000000000..62c7cce22834
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_mknod.sh
@@ -0,0 +1,143 @@
+# $NetBSD: t_mknod.sh,v 1.5 2010/11/07 17:51:18 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# Verifies that the mknod operation works.
+#
+
+atf_test_case block
+block_head() {
+ atf_set "descr" "Tests that block devices can be created"
+ atf_set "require.user" "root"
+}
+block_body() {
+ test_mount
+ umask 022
+
+ atf_check -s eq:0 -o empty -e empty mknod fd0a b 2 0
+ eval $(stat -s fd0a)
+ [ ${st_mode} = 060644 ] || atf_fail "Invalid mode"
+ [ ${st_rdev} -eq 512 ] || atf_fail "Invalid device"
+
+ test_unmount
+}
+
+atf_test_case block_kqueue
+block_kqueue_head() {
+ atf_set "descr" "Tests that creating a block device raises the" \
+ "appropriate kqueue events"
+ atf_set "require.user" "root"
+}
+block_kqueue_body() {
+ test_mount
+ umask 022
+
+ atf_check -s eq:0 -o empty -e empty mkdir dir
+ echo 'mknod dir/fd0a b 2 0' | kqueue_monitor 1 dir
+ kqueue_check dir NOTE_WRITE
+
+ test_unmount
+}
+
+atf_test_case char
+char_head() {
+ atf_set "descr" "Tests that character devices can be created"
+ atf_set "require.user" "root"
+}
+char_body() {
+ test_mount
+ umask 022
+
+ atf_check -s eq:0 -o empty -e empty mknod null c 2 2
+ eval $(stat -s null)
+ [ ${st_mode} = 020644 ] || atf_fail "Invalid mode"
+ [ ${st_rdev} -eq 514 ] || atf_fail "Invalid device"
+
+ test_unmount
+}
+
+atf_test_case char_kqueue
+char_kqueue_head() {
+ atf_set "descr" "Tests that creating a character device raises the" \
+ "appropriate kqueue events"
+ atf_set "require.user" "root"
+}
+char_kqueue_body() {
+ test_mount
+ umask 022
+
+ atf_check -s eq:0 -o empty -e empty mkdir dir
+ echo 'mknod dir/null c 2 2' | kqueue_monitor 1 dir
+ kqueue_check dir NOTE_WRITE
+
+ test_unmount
+}
+
+atf_test_case pipe
+pipe_head() {
+ atf_set "descr" "Tests that named pipes can be created"
+ atf_set "require.user" "root"
+}
+pipe_body() {
+ test_mount
+ umask 022
+
+ atf_check -s eq:0 -o empty -e empty mknod pipe p
+ eval $(stat -s pipe)
+ [ ${st_mode} = 010644 ] || atf_fail "Invalid mode"
+
+ test_unmount
+}
+
+atf_test_case pipe_kqueue
+pipe_kqueue_head() {
+ atf_set "descr" "Tests that creating a named pipe raises the" \
+ "appropriate kqueue events"
+ atf_set "require.user" "root"
+}
+pipe_kqueue_body() {
+ test_mount
+ umask 022
+
+ atf_check -s eq:0 -o empty -e empty mkdir dir
+ echo 'mknod dir/pipe p' | kqueue_monitor 1 dir
+ kqueue_check dir NOTE_WRITE
+
+ test_unmount
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case block
+ atf_add_test_case block_kqueue
+ atf_add_test_case char
+ atf_add_test_case char_kqueue
+ atf_add_test_case pipe
+ atf_add_test_case pipe_kqueue
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_mount.sh b/contrib/netbsd-tests/fs/tmpfs/t_mount.sh
new file mode 100755
index 000000000000..9ec5e31bd1a0
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_mount.sh
@@ -0,0 +1,156 @@
+# $NetBSD: t_mount.sh,v 1.6 2010/11/07 17:51:18 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# Verifies that an execution of mount and umount works correctly without
+# causing errors and that the root node gets correct attributes.
+# Also verifies command line parsing from mount_tmpfs.
+#
+
+atf_test_case plain
+plain_head() {
+ atf_set "descr" "Tests a mount and unmount without any options"
+ atf_set "require.user" "root"
+}
+plain_body() {
+ test_mount
+ test_unmount
+}
+
+atf_test_case links
+links_head() {
+ atf_set "descr" "Tests that the mount point has two hard links"
+ atf_set "require.user" "root"
+}
+links_body() {
+ test_mount
+ eval $(stat -s ${Mount_Point})
+ [ ${st_nlink} = 2 ] || \
+ atf_fail "Root directory does not have two hard links"
+ test_unmount
+}
+
+atf_test_case options
+options_head() {
+ atf_set "descr" "Tests the read-only mount option"
+ atf_set "require.user" "root"
+}
+options_body() {
+ test_mount -o ro
+ mount | grep ${Mount_Point} | grep -q read-only || \
+ atf_fail "read-only option (ro) does not work"
+ test_unmount
+}
+
+atf_test_case attrs
+attrs_head() {
+ atf_set "descr" "Tests that root directory attributes are set" \
+ "correctly"
+ atf_set "require.user" "root"
+}
+attrs_body() {
+ test_mount -o -u1000 -o -g100 -o -m755
+ eval $(stat -s ${Mount_Point})
+ [ ${st_uid} = 1000 ] || atf_fail "uid is incorrect"
+ [ ${st_gid} = 100 ] || atf_fail "gid is incorrect"
+ [ ${st_mode} = 040755 ] || atf_fail "mode is incorrect"
+ test_unmount
+}
+
+atf_test_case negative
+negative_head() {
+ atf_set "descr" "Tests that negative values passed to to -s are" \
+ "handled correctly"
+ atf_set "require.user" "root"
+}
+negative_body() {
+ mkdir tmp
+ test_mount -o -s-10
+ test_unmount
+}
+
+# Begin FreeBSD
+if true; then
+atf_test_case large cleanup
+large_cleanup() {
+ umount -f tmp 2>/dev/null || :
+ Mount_Point=$(pwd)/mnt _test_unmount || :
+}
+else
+# End FreeBSD
+atf_test_case large
+# Begin FreeBSD
+fi
+# End FreeBSD
+large_head() {
+ atf_set "descr" "Tests that extremely long values passed to -s" \
+ "are handled correctly"
+ atf_set "require.user" "root"
+}
+large_body() {
+ test_mount -o -s9223372036854775807
+ test_unmount
+
+ # Begin FreeBSD
+ atf_expect_fail "-o -s<large-size> succeeds unexpectedly on FreeBSD - bug 212862"
+ # End FreeBSD
+
+ mkdir tmp
+ atf_check -s eq:1 -o empty -e ignore \
+ mount -t tmpfs -o -s9223372036854775808 tmpfs tmp
+ atf_check -s eq:1 -o empty -e ignore \
+ mount -t tmpfs -o -s9223372036854775808g tmpfs tmp
+ rmdir tmp
+}
+
+atf_test_case mntpt
+mntpt_head() {
+ atf_set "descr" "Tests that the error messages printed when the" \
+ "mount point is invalid do not show the source" \
+ "unused parameter"
+}
+mntpt_body() {
+ mount_tmpfs unused $(pwd)/mnt >out 2>&1
+ atf_check -s eq:1 -o empty -e empty grep unused out
+ atf_check -s eq:0 -o ignore -e empty grep "$(pwd)/mnt" out
+
+ mount_tmpfs unused mnt >out 2>&1
+ atf_check -s eq:1 -o empty -e empty grep unused out
+ atf_check -s eq:0 -o ignore -e empty grep mnt out
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case plain
+ atf_add_test_case options
+ atf_add_test_case attrs
+ atf_add_test_case negative
+ atf_add_test_case large
+ atf_add_test_case mntpt
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_pipes.sh b/contrib/netbsd-tests/fs/tmpfs/t_pipes.sh
new file mode 100755
index 000000000000..7c0065a9cb1d
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_pipes.sh
@@ -0,0 +1,52 @@
+# $NetBSD: t_pipes.sh,v 1.5 2010/11/07 17:51:18 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+atf_test_case basic
+basic_head() {
+ atf_set "descr" "Verifies that pipes work"
+ atf_set "require.user" "root"
+}
+basic_body() {
+ test_mount
+
+ umask 022
+
+ atf_check -s eq:0 -o empty -e empty mknod pipe p
+
+ echo "Writing to pipe and waiting for response"
+ echo -n foo >pipe &
+ [ "$(cat pipe)" = foo ] || atf_fail "Received data is incorrect"
+
+ test_unmount
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case basic
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_read_write.sh b/contrib/netbsd-tests/fs/tmpfs/t_read_write.sh
new file mode 100755
index 000000000000..13cc2560ad44
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_read_write.sh
@@ -0,0 +1,87 @@
+# $NetBSD: t_read_write.sh,v 1.5 2010/11/07 17:51:18 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# Verifies that the read and write operations work.
+#
+
+atf_test_case basic
+basic_head() {
+ atf_set "descr" "Checks that file removal works"
+ atf_set "require.user" "root"
+}
+basic_body() {
+ test_mount
+
+ echo "Testing write to a small file"
+ echo foo >a || atf_fail "Failed to write to file"
+ [ $(md5 a | cut -d ' ' -f 4) = d3b07384d113edec49eaa6238ad5ff00 ] || \
+ atf_fail "Invalid file contents"
+
+ echo "Testing appending to a small file"
+ echo bar >>a || atf_fail "Failed to append data to file"
+ [ $(md5 a | cut -d ' ' -f 4) = f47c75614087a8dd938ba4acff252494 ] || \
+ atf_fail "Invalid file contents"
+
+ echo "Testing write to a big file (bigger than a page)"
+ jot 10000 >b || atf_fail "Failed to create a big file"
+ [ $(md5 b | cut -d ' ' -f 4) = 72d4ff27a28afbc066d5804999d5a504 ] || \
+ atf_fail "Invalid file contents"
+
+ test_unmount
+}
+
+atf_test_case kqueue
+kqueue_head() {
+ atf_set "descr" "Checks that writing to a file raises the" \
+ "appropriate kqueue events"
+ atf_set "require.user" "root"
+}
+kqueue_body() {
+ test_mount
+
+ atf_check -s eq:0 -o ignore -e ignore \
+ dd if=/dev/zero of=c bs=1k count=10
+ echo 'dd if=/dev/zero of=c seek=2 bs=1k count=1 conv=notrunc' \
+ '>/dev/null 2>&1' | kqueue_monitor 1 c
+ kqueue_check c NOTE_WRITE
+
+ echo foo >d
+ echo 'echo bar >>d' | kqueue_monitor 2 d
+ kqueue_check d NOTE_EXTEND
+ kqueue_check d NOTE_WRITE
+
+ test_unmount
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case basic
+ atf_add_test_case kqueue
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_readdir.sh b/contrib/netbsd-tests/fs/tmpfs/t_readdir.sh
new file mode 100755
index 000000000000..6f5dc3ef2bde
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_readdir.sh
@@ -0,0 +1,116 @@
+# $NetBSD: t_readdir.sh,v 1.5 2010/11/07 17:51:18 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# Verifies that the readdir operation works.
+#
+
+atf_test_case dots
+dots_head() {
+ atf_set "descr" "Verifies that readdir returns the '.' and '..'" \
+ "entries"
+ atf_set "require.user" "root"
+}
+dots_body() {
+ test_mount
+
+ atf_check -s eq:0 -o save:stdout -e empty /bin/ls -a
+ atf_check -s eq:0 -o ignore -e empty grep '^\.$' stdout
+ atf_check -s eq:0 -o ignore -e empty grep '^\..$' stdout
+
+ test_unmount
+}
+
+atf_test_case types
+types_head() {
+ atf_set "descr" "Verifies that readdir works for all different" \
+ "file types"
+ atf_set "require.user" "root"
+}
+types_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir dir
+ atf_check -s eq:0 -o empty -e empty touch reg
+ atf_check -s eq:0 -o empty -e empty ln -s reg lnk
+ atf_check -s eq:0 -o empty -e empty mknod blk b 0 0
+ atf_check -s eq:0 -o empty -e empty mknod chr c 0 0
+ atf_check -s eq:0 -o empty -e empty mknod fifo p
+ atf_check -s eq:0 -o empty -e empty \
+ $(atf_get_srcdir)/h_tools sockets sock
+
+ atf_check -s eq:0 -o ignore -e empty ls
+ atf_check -s eq:0 -o empty -e empty rm -rf *
+
+ test_unmount
+}
+
+atf_test_case caching
+caching_head() {
+ atf_set "descr" "Catch a bug caused by incorrect invalidation of" \
+ "readdir caching variables"
+ atf_set "require.user" "root"
+}
+caching_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty touch $(jot 10)
+ atf_check -s eq:0 -o empty -e empty rm *
+ atf_check -s eq:0 -o empty -e empty touch $(jot 20)
+ atf_check -s eq:0 -o empty -e empty -x "ls >/dev/null"
+
+ test_unmount
+}
+
+atf_test_case many
+many_head() {
+ atf_set "descr" "Verifies that readdir works with many files"
+ atf_set "require.user" "root"
+}
+many_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir a
+ echo "Creating 500 files"
+ for f in $(jot 500); do
+ touch a/$f
+ done
+ atf_check -s eq:0 -o empty -e empty rm a/*
+ atf_check -s eq:0 -o empty -e empty rmdir a
+
+ test_unmount
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case dots
+ atf_add_test_case types
+ atf_add_test_case caching
+ atf_add_test_case many
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_remove.sh b/contrib/netbsd-tests/fs/tmpfs/t_remove.sh
new file mode 100755
index 000000000000..f75032c0b146
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_remove.sh
@@ -0,0 +1,125 @@
+# $NetBSD: t_remove.sh,v 1.5 2010/11/07 17:51:18 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# Verifies that the remove operation works.
+#
+
+atf_test_case single
+single_head() {
+ atf_set "descr" "Checks that file removal works"
+ atf_set "require.user" "root"
+}
+single_body() {
+ test_mount
+
+ atf_check -s eq:1 -o empty -e empty test -f a
+ atf_check -s eq:0 -o empty -e empty touch a
+ atf_check -s eq:0 -o empty -e empty test -f a
+ atf_check -s eq:0 -o empty -e empty rm a
+ atf_check -s eq:1 -o empty -e empty test -f a
+
+ test_unmount
+}
+
+# Begin FreeBSD
+if true; then
+atf_test_case uchg cleanup
+uchg_cleanup() {
+ Mount_Point=$(pwd)/mntpt _test_unmount
+}
+else
+# End FreeBSD
+atf_test_case uchg
+# Begin FreeBSD
+fi
+# End FreeBSD
+uchg_head() {
+ atf_set "descr" "Checks that files with the uchg flag set cannot" \
+ "be removed"
+ atf_set "require.user" "root"
+}
+uchg_body() {
+ # Begin FreeBSD
+ atf_expect_fail "this fails on FreeBSD with root - bug 212861"
+ # End FreeBSD
+
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty touch a
+ atf_check -s eq:0 -o empty -e empty chflags uchg a
+ atf_check -s eq:1 -o empty -e ignore rm -f a
+ atf_check -s eq:0 -o empty -e empty chflags nouchg a
+ atf_check -s eq:0 -o empty -e empty rm a
+ atf_check -s eq:1 -o empty -e empty test -f a
+
+ test_unmount
+}
+
+atf_test_case dot
+dot_head() {
+ atf_set "descr" "Checks that '.' cannot be removed"
+ atf_set "require.user" "root"
+}
+dot_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir a
+ atf_check -s eq:1 -o empty -e ignore unlink a/.
+ atf_check -s eq:0 -o empty -e empty rmdir a
+
+ test_unmount
+}
+
+atf_test_case kqueue
+kqueue_head() {
+ atf_set "descr" "Removes a file and checks the kqueue events" \
+ "raised"
+ atf_set "require.user" "root"
+}
+kqueue_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir dir
+ atf_check -s eq:0 -o empty -e empty touch dir/a
+ echo 'rm dir/a' | kqueue_monitor 2 dir dir/a
+ kqueue_check dir/a NOTE_DELETE
+ kqueue_check dir NOTE_WRITE
+ atf_check -s eq:0 -o empty -e empty rmdir dir
+
+ test_unmount
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case single
+ atf_add_test_case uchg
+ atf_add_test_case dot
+ atf_add_test_case kqueue
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_rename.sh b/contrib/netbsd-tests/fs/tmpfs/t_rename.sh
new file mode 100755
index 000000000000..7613f1f493a8
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_rename.sh
@@ -0,0 +1,278 @@
+# $NetBSD: t_rename.sh,v 1.5 2010/11/07 17:51:18 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# Verifies that the rename operation works (either by renaming entries or
+# by moving them).
+#
+
+atf_test_case dots
+dots_head() {
+ atf_set "descr" "Tests that '.' and '..' cannot be renamed"
+ atf_set "require.user" "root"
+}
+dots_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir a
+ atf_check -s eq:1 -o empty -e ignore mv a/. c
+ atf_check -s eq:1 -o empty -e ignore mv a/.. c
+ atf_check -s eq:0 -o empty -e empty rmdir a
+
+ test_unmount
+}
+
+atf_test_case crossdev
+crossdev_head() {
+ atf_set "descr" "Tests that cross-device renames do not work"
+ atf_set "require.user" "root"
+}
+crossdev_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir a
+ atf_check -s eq:1 -o empty -e save:stderr \
+ $(atf_get_srcdir)/h_tools rename a /var/tmp/a
+ atf_check -s eq:0 -o ignore -e empty grep "Cross-device link" stderr
+ atf_check -s eq:0 -o empty -e empty test -d a
+ atf_check -s eq:0 -o empty -e empty rmdir a
+
+ test_unmount
+}
+
+atf_test_case basic
+basic_head() {
+ atf_set "descr" "Tests that basic renames work"
+ atf_set "require.user" "root"
+}
+basic_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir a
+ atf_check -s eq:0 -o empty -e empty mv a c
+ atf_check -s eq:1 -o empty -e empty test -d a
+ atf_check -s eq:0 -o empty -e empty test -d c
+ atf_check -s eq:0 -o empty -e empty rmdir c
+
+ test_unmount
+}
+
+atf_test_case dotdot
+dotdot_head() {
+ atf_set "descr" "Tests that the '..' entry is properly updated" \
+ "during moves"
+ atf_set "require.user" "root"
+}
+dotdot_body() {
+ test_mount
+
+ echo "Checking if the '..' entry is updated after moves"
+ atf_check -s eq:0 -o empty -e empty mkdir a
+ atf_check -s eq:0 -o empty -e empty mkdir b
+ atf_check -s eq:0 -o empty -e empty mv b a
+ atf_check -s eq:0 -o empty -e empty test -d a/b/../b
+ atf_check -s eq:0 -o empty -e empty test -d a/b/../../a
+ eval $(stat -s a/b)
+ [ ${st_nlink} = 2 ] || atf_fail "Incorrect number of links"
+ eval $(stat -s a)
+ [ ${st_nlink} = 3 ] || atf_fail "Incorrect number of links"
+ atf_check -s eq:0 -o empty -e empty rmdir a/b
+ atf_check -s eq:0 -o empty -e empty rmdir a
+
+ echo "Checking if the '..' entry is correct after renames"
+ atf_check -s eq:0 -o empty -e empty mkdir a
+ atf_check -s eq:0 -o empty -e empty mkdir b
+ atf_check -s eq:0 -o empty -e empty mv b a
+ atf_check -s eq:0 -o empty -e empty mv a c
+ atf_check -s eq:0 -o empty -e empty test -d c/b/../b
+ atf_check -s eq:0 -o empty -e empty test -d c/b/../../c
+ atf_check -s eq:0 -o empty -e empty rmdir c/b
+ atf_check -s eq:0 -o empty -e empty rmdir c
+
+ echo "Checking if the '..' entry is correct after multiple moves"
+ atf_check -s eq:0 -o empty -e empty mkdir a
+ atf_check -s eq:0 -o empty -e empty mkdir b
+ atf_check -s eq:0 -o empty -e empty mv b a
+ atf_check -s eq:0 -o empty -e empty mv a c
+ atf_check -s eq:0 -o empty -e empty mv c/b d
+ atf_check -s eq:0 -o empty -e empty test -d d/../c
+ atf_check -s eq:0 -o empty -e empty rmdir d
+ atf_check -s eq:0 -o empty -e empty rmdir c
+
+ test_unmount
+}
+
+atf_test_case dir_to_emptydir
+dir_to_emptydir_head() {
+ atf_set "descr" "Tests that renaming a directory to override an" \
+ "empty directory works"
+ atf_set "require.user" "root"
+}
+dir_to_emptydir_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir a
+ atf_check -s eq:0 -o empty -e empty touch a/c
+ atf_check -s eq:0 -o empty -e empty mkdir b
+ atf_check -s eq:0 -o empty -e empty \
+ $(atf_get_srcdir)/h_tools rename a b
+ atf_check -s eq:1 -o empty -e empty test -e a
+ atf_check -s eq:0 -o empty -e empty test -d b
+ atf_check -s eq:0 -o empty -e empty test -f b/c
+ rm b/c
+ rmdir b
+
+ test_unmount
+}
+
+atf_test_case dir_to_fulldir
+dir_to_fulldir_head() {
+ atf_set "descr" "Tests that renaming a directory to override a" \
+ "non-empty directory fails"
+ atf_set "require.user" "root"
+}
+dir_to_fulldir_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir a
+ atf_check -s eq:0 -o empty -e empty touch a/c
+ atf_check -s eq:0 -o empty -e empty mkdir b
+ atf_check -s eq:0 -o empty -e empty touch b/d
+ atf_check -s eq:1 -o empty -e save:stderr \
+ $(atf_get_srcdir)/h_tools rename a b
+ atf_check -s eq:0 -o ignore -e empty grep "Directory not empty" stderr
+ atf_check -s eq:0 -o empty -e empty test -d a
+ atf_check -s eq:0 -o empty -e empty test -f a/c
+ atf_check -s eq:0 -o empty -e empty test -d b
+ atf_check -s eq:0 -o empty -e empty test -f b/d
+ rm a/c
+ rm b/d
+ rmdir a
+ rmdir b
+
+ test_unmount
+}
+
+atf_test_case dir_to_file
+dir_to_file_head() {
+ atf_set "descr" "Tests that renaming a directory to override a" \
+ "file fails"
+ atf_set "require.user" "root"
+}
+dir_to_file_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir a
+ atf_check -s eq:0 -o empty -e empty touch b
+ atf_check -s eq:1 -o empty -e save:stderr \
+ $(atf_get_srcdir)/h_tools rename a b
+ atf_check -s eq:0 -o ignore -e empty grep "Not a directory" stderr
+ atf_check -s eq:0 -o empty -e empty test -d a
+ atf_check -s eq:0 -o empty -e empty test -f b
+ rmdir a
+ rm b
+
+ test_unmount
+}
+
+atf_test_case file_to_dir
+file_to_dir_head() {
+ atf_set "descr" "Tests that renaming a file to override a" \
+ "directory fails"
+ atf_set "require.user" "root"
+}
+file_to_dir_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty touch a
+ atf_check -s eq:0 -o empty -e empty mkdir b
+ atf_check -s eq:1 -o empty -e save:stderr \
+ $(atf_get_srcdir)/h_tools rename a b
+ atf_check -s eq:0 -o ignore -e empty grep "Is a directory" stderr
+ atf_check -s eq:0 -o empty -e empty test -f a
+ atf_check -s eq:0 -o empty -e empty test -d b
+ rm a
+ rmdir b
+
+ test_unmount
+}
+
+atf_test_case kqueue
+kqueue_head() {
+ atf_set "descr" "Tests that moving and renaming files raise the" \
+ "correct kqueue events"
+ atf_set "require.user" "root"
+}
+kqueue_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir dir
+ atf_check -s eq:0 -o empty -e empty touch dir/a
+ echo 'mv dir/a dir/b' | kqueue_monitor 2 dir dir/a
+ kqueue_check dir/a NOTE_RENAME
+ kqueue_check dir NOTE_WRITE
+ atf_check -s eq:0 -o empty -e empty rm dir/b
+ atf_check -s eq:0 -o empty -e empty rmdir dir
+
+ atf_check -s eq:0 -o empty -e empty mkdir dir
+ atf_check -s eq:0 -o empty -e empty touch dir/a
+ atf_check -s eq:0 -o empty -e empty touch dir/b
+ echo 'mv dir/a dir/b' | kqueue_monitor 3 dir dir/a dir/b
+ kqueue_check dir/a NOTE_RENAME
+ kqueue_check dir NOTE_WRITE
+ kqueue_check dir/b NOTE_DELETE
+ atf_check -s eq:0 -o empty -e empty rm dir/b
+ atf_check -s eq:0 -o empty -e empty rmdir dir
+
+ atf_check -s eq:0 -o empty -e empty mkdir dir1
+ atf_check -s eq:0 -o empty -e empty mkdir dir2
+ atf_check -s eq:0 -o empty -e empty touch dir1/a
+ echo 'mv dir1/a dir2/a' | kqueue_monitor 3 dir1 dir1/a dir2
+ kqueue_check dir1/a NOTE_RENAME
+ kqueue_check dir1 NOTE_WRITE
+ kqueue_check dir2 NOTE_WRITE
+ atf_check -s eq:0 -o empty -e empty rm dir2/a
+ atf_check -s eq:0 -o empty -e empty rmdir dir1
+ atf_check -s eq:0 -o empty -e empty rmdir dir2
+
+ test_unmount
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case dots
+ atf_add_test_case crossdev
+ atf_add_test_case basic
+ atf_add_test_case dotdot
+ atf_add_test_case dir_to_emptydir
+ atf_add_test_case dir_to_fulldir
+ atf_add_test_case dir_to_file
+ atf_add_test_case file_to_dir
+ atf_add_test_case kqueue
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_renamerace.c b/contrib/netbsd-tests/fs/tmpfs/t_renamerace.c
new file mode 100644
index 000000000000..d7c10e4c6679
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_renamerace.c
@@ -0,0 +1,115 @@
+/* $NetBSD: t_renamerace.c,v 1.14 2017/01/13 21:30:40 christos Exp $ */
+
+/*
+ * Modified for rump and atf from a program supplied
+ * by Nicolas Joly in kern/40948
+ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/utsname.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include <fs/tmpfs/tmpfs_args.h>
+
+#include "h_macros.h"
+
+ATF_TC(renamerace2);
+ATF_TC_HEAD(renamerace2, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "rename(2) lock order inversion");
+ atf_tc_set_md_var(tc, "timeout", "6");
+}
+
+static volatile int quittingtime = 0;
+static pid_t wrkpid;
+
+static void *
+r2w1(void *arg)
+{
+ int fd;
+
+ rump_pub_lwproc_newlwp(wrkpid);
+
+ fd = rump_sys_open("/file", O_CREAT | O_RDWR, 0777);
+ if (fd == -1)
+ atf_tc_fail_errno("creat");
+ rump_sys_close(fd);
+
+ while (!quittingtime) {
+ if (rump_sys_rename("/file", "/dir/file") == -1)
+ atf_tc_fail_errno("rename 1");
+ if (rump_sys_rename("/dir/file", "/file") == -1)
+ atf_tc_fail_errno("rename 2");
+ }
+
+ return NULL;
+}
+
+static void *
+r2w2(void *arg)
+{
+ int fd;
+
+ rump_pub_lwproc_newlwp(wrkpid);
+
+ while (!quittingtime) {
+ fd = rump_sys_open("/dir/file1", O_RDWR);
+ if (fd != -1)
+ rump_sys_close(fd);
+ }
+
+ return NULL;
+}
+
+ATF_TC_BODY(renamerace2, tc)
+{
+ struct tmpfs_args args;
+ pthread_t pt[2];
+
+ /*
+ * Force SMP regardless of how many host CPUs there are.
+ * Deadlock is highly unlikely to trigger otherwise.
+ */
+ setenv("RUMP_NCPU", "2", 1);
+
+ rump_init();
+ memset(&args, 0, sizeof(args));
+ args.ta_version = TMPFS_ARGS_VERSION;
+ args.ta_root_mode = 0777;
+ if (rump_sys_mount(MOUNT_TMPFS, "/", 0, &args, sizeof(args)) == -1)
+ atf_tc_fail_errno("could not mount tmpfs");
+
+ if (rump_sys_mkdir("/dir", 0777) == -1)
+ atf_tc_fail_errno("cannot create directory");
+
+ RZ(rump_pub_lwproc_rfork(RUMP_RFCFDG));
+ RL(wrkpid = rump_sys_getpid());
+ pthread_create(&pt[0], NULL, r2w1, NULL);
+ pthread_create(&pt[1], NULL, r2w2, NULL);
+
+ /* usually triggers in <<1s for me */
+ sleep(4);
+ quittingtime = 1;
+
+ pthread_join(pt[0], NULL);
+ pthread_join(pt[1], NULL);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, renamerace2);
+
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_rmdir.sh b/contrib/netbsd-tests/fs/tmpfs/t_rmdir.sh
new file mode 100755
index 000000000000..9e5d761edf6c
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_rmdir.sh
@@ -0,0 +1,204 @@
+# $NetBSD: t_rmdir.sh,v 1.5 2010/11/07 17:51:18 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# Verifies that rmdir works by creating and removing directories. Also
+# checks multiple error conditions.
+#
+
+atf_test_case mntpt
+mntpt_head() {
+ atf_set "descr" "Checks that the mount point cannot be removed"
+ atf_set "require.user" "root"
+}
+mntpt_body() {
+ test_mount
+
+ atf_check -s eq:1 -o empty -e ignore rmdir ${Mount_Point}
+
+ test_unmount
+}
+
+atf_test_case non_existent
+non_existent_head() {
+ atf_set "descr" "Checks that non-existent directories cannot" \
+ "be removed"
+ atf_set "require.user" "root"
+}
+non_existent_body() {
+ test_mount
+
+ atf_check -s eq:1 -o empty -e ignore rmdir non-existent
+
+ test_unmount
+}
+
+atf_test_case single
+single_head() {
+ atf_set "descr" "Checks that removing a single directory works"
+ atf_set "require.user" "root"
+}
+single_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir a
+ eval $(stat -s ${Mount_Point})
+ [ ${st_nlink} = 3 ] || \
+ atf_fail "Incorrect number of links after creation"
+ atf_check -s eq:0 -o empty -e empty rmdir a
+ eval $(stat -s ${Mount_Point})
+ [ ${st_nlink} = 2 ] || \
+ atf_fail "Incorrect number of links after removal"
+
+ test_unmount
+}
+
+atf_test_case nested
+nested_head() {
+ atf_set "descr" "Checks that removing nested directories works"
+ atf_set "require.user" "root"
+}
+nested_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir -p a/b/c
+ atf_check -s eq:0 -o empty -e empty rmdir a/b/c
+ atf_check -s eq:0 -o empty -e empty rmdir a/b
+ atf_check -s eq:0 -o empty -e empty rmdir a
+
+ test_unmount
+}
+
+atf_test_case dots
+dots_head() {
+ atf_set "descr" "Checks that '.' and '..' cannot be removed"
+ atf_set "require.user" "root"
+}
+dots_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir a
+ atf_check -s eq:1 -o empty -e ignore rmdir a/.
+ atf_check -s eq:1 -o empty -e ignore rmdir a/..
+ atf_check -s eq:0 -o empty -e empty rmdir a
+
+ test_unmount
+}
+
+atf_test_case non_empty
+non_empty_head() {
+ atf_set "descr" "Checks that non-empty directories cannot be removed"
+ atf_set "require.user" "root"
+}
+non_empty_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir a
+ atf_check -s eq:0 -o empty -e empty mkdir a/b
+ atf_check -s eq:0 -o empty -e empty mkdir a/c
+ atf_check -s eq:1 -o empty -e ignore rmdir a
+ atf_check -s eq:0 -o empty -e empty rmdir a/b
+ atf_check -s eq:0 -o empty -e empty rmdir a/c
+ atf_check -s eq:0 -o empty -e empty rmdir a
+
+ test_unmount
+}
+
+atf_test_case links
+links_head() {
+ atf_set "descr" "Checks the root directory's links after removals"
+ atf_set "require.user" "root"
+}
+links_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir a
+ atf_check -s eq:0 -o empty -e empty mkdir a/b
+ atf_check -s eq:0 -o empty -e empty mkdir c
+
+ atf_check -s eq:0 -o empty -e empty rmdir c
+ atf_check -s eq:0 -o empty -e empty rmdir a/b
+ atf_check -s eq:0 -o empty -e empty rmdir a
+
+ eval $(stat -s ${Mount_Point})
+ [ ${st_nlink} = 2 ] || atf_fail "Incorrect number of links"
+
+ test_unmount
+}
+
+atf_test_case curdir
+curdir_head() {
+ atf_set "descr" "Checks that the current directory cannot be removed"
+ atf_set "require.user" "root"
+}
+curdir_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir a
+ # Catch a bug that would panic the system when accessing the
+ # current directory after being deleted: vop_open cannot assume
+ # that open files are still linked to a directory.
+ atf_check -s eq:1 -o empty -e ignore -x '( cd a && rmdir ../a && ls )'
+ atf_check -s eq:1 -o empty -e empty test -e a
+
+ test_unmount
+}
+
+atf_test_case kqueue
+kqueue_head() {
+ atf_set "descr" "Checks that removing a directory raises the" \
+ "correct kqueue events"
+ atf_set "require.user" "root"
+}
+kqueue_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir dir
+ atf_check -s eq:0 -o empty -e empty mkdir dir/a
+ echo 'rmdir dir/a' | kqueue_monitor 3 dir dir/a
+ kqueue_check dir/a NOTE_DELETE
+ kqueue_check dir NOTE_LINK
+ kqueue_check dir NOTE_WRITE
+ atf_check -s eq:0 -o empty -e empty rmdir dir
+
+ test_unmount
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case mntpt
+ atf_add_test_case non_existent
+ atf_add_test_case single
+ atf_add_test_case nested
+ atf_add_test_case dots
+ atf_add_test_case non_empty
+ atf_add_test_case links
+ atf_add_test_case curdir
+ atf_add_test_case kqueue
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_setattr.sh b/contrib/netbsd-tests/fs/tmpfs/t_setattr.sh
new file mode 100755
index 000000000000..f64344646e48
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_setattr.sh
@@ -0,0 +1,216 @@
+# $NetBSD: t_setattr.sh,v 1.5 2010/11/07 17:51:18 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# Verifies that the setattr vnode operation works, using several commands
+# that require this function.
+#
+
+atf_test_case chown
+chown_head() {
+ atf_set "descr" "Tests that the file owner can be changed"
+ atf_set "require.user" "root"
+}
+chown_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir own
+ eval $(stat -s own | sed -e 's|st_|ost_|g')
+ atf_check -s eq:0 -o empty -e empty chown 1234 own
+ eval $(stat -s own)
+ [ ${st_uid} -eq 1234 ] || atf_fail "uid was not set"
+ [ ${st_gid} -eq ${ost_gid} ] || atf_fail "gid was modified"
+
+ test_unmount
+}
+
+atf_test_case chown_kqueue
+chown_kqueue_head() {
+ atf_set "descr" "Tests that changing the file owner raises" \
+ "NOTE_ATTRIB on it"
+ atf_set "require.user" "root"
+}
+chown_kqueue_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir ownq
+ echo 'chown 1234 ownq' | kqueue_monitor 1 ownq
+ kqueue_check ownq NOTE_ATTRIB
+
+ test_unmount
+}
+
+atf_test_case chgrp
+chgrp_head() {
+ atf_set "descr" "Tests that the file group can be changed"
+ atf_set "require.user" "root"
+}
+chgrp_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir grp
+ eval $(stat -s grp | sed -e 's|st_|ost_|g')
+ atf_check -s eq:0 -o empty -e empty chgrp 5678 grp
+ eval $(stat -s grp)
+ [ ${st_uid} -eq ${ost_uid} ] || atf_fail "uid was modified"
+ [ ${st_gid} -eq 5678 ] || atf_fail "gid was not set"
+
+ test_unmount
+}
+
+atf_test_case chgrp_kqueue
+chgrp_kqueue_head() {
+ atf_set "descr" "Tests that changing the file group raises" \
+ "NOTE_ATTRIB on it"
+ atf_set "require.user" "root"
+}
+chgrp_kqueue_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir grpq
+ echo 'chgrp 1234 grpq' | kqueue_monitor 1 grpq
+ kqueue_check grpq NOTE_ATTRIB
+
+ test_unmount
+}
+
+atf_test_case chowngrp
+chowngrp_head() {
+ atf_set "descr" "Tests that the file owner and group can be" \
+ "changed at once"
+ atf_set "require.user" "root"
+}
+chowngrp_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir owngrp
+ atf_check -s eq:0 -o empty -e empty chown 1234:5678 owngrp
+ eval $(stat -s owngrp)
+ [ ${st_uid} -eq 1234 ] || atf_fail "uid was not modified"
+ [ ${st_gid} -eq 5678 ] || atf_fail "gid was not modified"
+
+ test_unmount
+}
+
+atf_test_case chowngrp_kqueue
+chowngrp_kqueue_head() {
+ atf_set "descr" "Tests that changing the file owner and group" \
+ "raises NOTE_ATTRIB on it"
+ atf_set "require.user" "root"
+}
+chowngrp_kqueue_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir owngrpp
+ echo 'chown 1234:5678 owngrpp' | kqueue_monitor 1 owngrpp
+ kqueue_check owngrpp NOTE_ATTRIB
+
+ test_unmount
+}
+
+atf_test_case chmod
+chmod_head() {
+ atf_set "descr" "Tests that the file mode can be changed"
+ atf_set "require.user" "root"
+}
+chmod_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir mode
+ atf_check -s eq:0 -o empty -e empty chmod 0000 mode
+ eval $(stat -s mode)
+ [ ${st_mode} -eq 40000 ] || af_fail "mode was not set"
+
+ test_unmount
+}
+
+atf_test_case chmod_kqueue
+chmod_kqueue_head() {
+ atf_set "descr" "Tests that changing the file mode raises" \
+ "NOTE_ATTRIB on it"
+ atf_set "require.user" "root"
+}
+chmod_kqueue_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir modeq
+ echo 'chmod 0000 modeq' | kqueue_monitor 1 modeq
+ kqueue_check modeq NOTE_ATTRIB
+
+ test_unmount
+}
+
+atf_test_case chtimes
+chtimes_head() {
+ atf_set "descr" "Tests that file times can be changed"
+ atf_set "require.user" "root"
+}
+chtimes_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir times
+ atf_check -s eq:0 -o empty -e empty \
+ -x 'TZ=GMT touch -t 200501010101 times'
+ eval $(stat -s times)
+ [ ${st_atime} = ${st_mtime} ] || \
+ atf_fail "atime does not match mtime"
+ [ ${st_atime} = 1104541260 ] || atf_fail "atime does not match"
+
+ test_unmount
+}
+
+atf_test_case chtimes_kqueue
+chtimes_kqueue_head() {
+ atf_set "descr" "Tests that changing the file times raises" \
+ "NOTE_ATTRIB on it"
+ atf_set "require.user" "root"
+}
+chtimes_kqueue_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir timesq
+ echo 'touch timesq' | kqueue_monitor 1 timesq
+ kqueue_check timesq NOTE_ATTRIB
+
+ test_unmount
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case chown
+ atf_add_test_case chown_kqueue
+ atf_add_test_case chgrp
+ atf_add_test_case chgrp_kqueue
+ atf_add_test_case chowngrp
+ atf_add_test_case chowngrp_kqueue
+ atf_add_test_case chmod
+ atf_add_test_case chmod_kqueue
+ atf_add_test_case chtimes
+ atf_add_test_case chtimes_kqueue
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_sizes.sh b/contrib/netbsd-tests/fs/tmpfs/t_sizes.sh
new file mode 100755
index 000000000000..35abe8ac25fe
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_sizes.sh
@@ -0,0 +1,139 @@
+# $NetBSD: t_sizes.sh,v 1.5 2010/11/07 17:51:18 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# Verifies that the file system controls memory usage correctly.
+#
+
+atf_test_case small
+small_head() {
+ atf_set "descr" "Checks the status after creating a small file"
+ atf_set "require.user" "root"
+}
+small_body() {
+ test_mount -o -s10M
+
+ echo a >a || atf_fail "Could not create file"
+ eval $($(atf_get_srcdir)/h_tools statvfs .)
+ f_bused=$((${f_blocks} - ${f_bfree}))
+ [ ${f_bused} -gt 1 ] || atf_fail "Incorrect bused count"
+ atf_check -s eq:0 -o empty -e empty rm a
+
+ test_unmount
+}
+
+atf_test_case big
+big_head() {
+ atf_set "descr" "Checks the status after creating a big file"
+ atf_set "require.user" "root"
+}
+big_body() {
+ test_mount -o -s10M
+
+ # Begin FreeBSD
+ if true; then
+ pagesize=$(sysctl -n hw.pagesize)
+ else
+ # End FreeBSD
+ pagesize=$(sysctl hw.pagesize | cut -d ' ' -f 3)
+ # Begin FreeBSD
+ fi
+ # End FreeBSD
+ eval $($(atf_get_srcdir)/h_tools statvfs . | sed -e 's|^f_|cf_|')
+ cf_bused=$((${cf_blocks} - ${cf_bfree}))
+
+ atf_check -s eq:0 -o ignore -e ignore \
+ dd if=/dev/zero of=a bs=1m count=5
+ eval $($(atf_get_srcdir)/h_tools statvfs .)
+ f_bused=$((${f_blocks} - ${f_bfree}))
+ [ ${f_bused} -ne ${cf_bused} ] || atf_fail "bused did not change"
+ [ ${f_bused} -gt $((5 * 1024 * 1024 / ${pagesize})) ] || \
+ atf_fail "bused too big"
+ of_bused=${f_bused}
+ atf_check -s eq:0 -o empty -e empty rm a
+ eval $($(atf_get_srcdir)/h_tools statvfs .)
+ f_bused=$((${f_blocks} - ${f_bfree}))
+ [ ${f_bused} -lt ${of_bused} ] || \
+ atf_fail "bused was not correctly restored"
+
+ test_unmount
+}
+
+atf_test_case overflow
+overflow_head() {
+ atf_set "descr" "Checks the status after creating a big file that" \
+ "overflows the file system limits"
+ atf_set "require.user" "root"
+}
+overflow_body() {
+ test_mount -o -s10M
+
+ atf_check -s eq:0 -o empty -e empty touch a
+ atf_check -s eq:0 -o empty -e empty rm a
+ eval $($(atf_get_srcdir)/h_tools statvfs .)
+ of_bused=$((${f_blocks} - ${f_bfree}))
+ atf_check -s eq:1 -o ignore -e ignore \
+ dd if=/dev/zero of=a bs=1m count=15
+ atf_check -s eq:0 -o empty -e empty rm a
+ eval $($(atf_get_srcdir)/h_tools statvfs .)
+ f_bused=$((${f_blocks} - ${f_bfree}))
+ [ ${f_bused} -ge ${of_bused} -a ${f_bused} -le $((${of_bused} + 1)) ] \
+ || atf_fail "Incorrect bused"
+
+ test_unmount
+}
+
+atf_test_case overwrite
+overwrite_head() {
+ atf_set "descr" "Checks that writing to the middle of a file" \
+ "does not change its size"
+ atf_set "require.user" "root"
+}
+overwrite_body() {
+ test_mount -o -s10M
+
+ atf_check -s eq:0 -o ignore -e ignore \
+ dd if=/dev/zero of=a bs=1024 count=10
+ sync
+ atf_check -s eq:0 -o ignore -e ignore \
+ dd if=/dev/zero of=a bs=1024 conv=notrunc seek=1 count=1
+ sync
+ eval $(stat -s a)
+ [ ${st_size} -eq 10240 ] || atf_fail "Incorrect file size"
+
+ test_unmount
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case small
+ atf_add_test_case big
+ atf_add_test_case overflow
+ atf_add_test_case overwrite
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_sockets.sh b/contrib/netbsd-tests/fs/tmpfs/t_sockets.sh
new file mode 100755
index 000000000000..6b972483513c
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_sockets.sh
@@ -0,0 +1,52 @@
+# $NetBSD: t_sockets.sh,v 1.5 2010/11/07 17:51:18 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+atf_test_case basic
+basic_head() {
+ atf_set "descr" "Verifies that sockets can be created using" \
+ "socket/bind"
+ atf_set "require.user" "root"
+}
+basic_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty $(atf_get_srcdir)/h_tools sockets a
+ atf_check -s eq:0 -o empty -e empty rm a
+
+ atf_check -s eq:0 -o empty -e empty mkdir dir
+ echo "$(atf_get_srcdir)/h_tools sockets dir/a" | kqueue_monitor 1 dir
+ kqueue_check dir NOTE_WRITE
+
+ test_unmount
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case basic
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_statvfs.sh b/contrib/netbsd-tests/fs/tmpfs/t_statvfs.sh
new file mode 100755
index 000000000000..d0e7ac27ebb9
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_statvfs.sh
@@ -0,0 +1,67 @@
+# $NetBSD: t_statvfs.sh,v 1.4 2010/11/07 17:51:18 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# Verifies that the statvfs system call works properly (returning the
+# correct values) over a tmpfs mount point.
+#
+
+atf_test_case values
+values_head() {
+ atf_set "descr" "Tests that statvfs(2) returns correct values"
+ atf_set "require.user" "root"
+}
+values_body() {
+ test_mount -o -s10M
+
+ # Begin FreeBSD
+ if true; then
+ pagesize=$(sysctl -n hw.pagesize)
+ else
+ # End FreeBSD
+ pagesize=$(sysctl hw.pagesize | cut -d ' ' -f 3)
+ # Begin FreeBSD
+ fi
+ # End FreeBSD
+ eval $($(atf_get_srcdir)/h_tools statvfs .)
+ [ ${pagesize} -eq ${f_bsize} ] || \
+ atf_fail "Invalid bsize"
+ [ $((${f_bsize} * ${f_blocks})) -ge $((10 * 1024 * 1024)) ] || \
+ atf_file "bsize * blocks too small"
+ [ $((${f_bsize} * ${f_blocks})) -le \
+ $((10 * 1024 * 1024 + ${pagesize})) ] || \
+ atf_fail "bsize * blocks too big"
+
+ test_unmount
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case values
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_symlink.sh b/contrib/netbsd-tests/fs/tmpfs/t_symlink.sh
new file mode 100755
index 000000000000..2cc66c06b4a6
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_symlink.sh
@@ -0,0 +1,114 @@
+# $NetBSD: t_symlink.sh,v 1.5 2010/11/07 17:51:18 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# Verifies that the symlink and readlink operations work.
+#
+
+atf_test_case file
+file_head() {
+ atf_set "descr" "Tests that symlinks to files work"
+ atf_set "require.user" "root"
+}
+file_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty touch a
+ atf_check -s eq:0 -o empty -e empty ln -s a b
+ [ $(md5 b | cut -d ' ' -f 4) = d41d8cd98f00b204e9800998ecf8427e ] || \
+ atf_fail "Symlink points to an incorrect file"
+
+ atf_check -s eq:0 -o empty -e empty -x 'echo foo >a'
+ [ $(md5 b | cut -d ' ' -f 4) = d3b07384d113edec49eaa6238ad5ff00 ] || \
+ atf_fail "Symlink points to an incorrect file"
+
+ test_unmount
+}
+
+atf_test_case exec
+exec_head() {
+ atf_set "descr" "Tests symlinking to a known system binary and" \
+ "executing it through the symlink"
+ atf_set "require.user" "root"
+}
+exec_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty touch b
+ atf_check -s eq:0 -o empty -e empty ln -s /bin/cp cp
+ atf_check -s eq:0 -o empty -e empty ./cp b c
+ atf_check -s eq:0 -o empty -e empty test -f c
+
+ test_unmount
+}
+
+atf_test_case dir
+dir_head() {
+ atf_set "descr" "Tests that symlinks to directories work"
+ atf_set "require.user" "root"
+}
+dir_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir d
+ atf_check -s eq:1 -o empty -e empty test -f d/foo
+ atf_check -s eq:1 -o empty -e empty test -f e/foo
+ atf_check -s eq:0 -o empty -e empty ln -s d e
+ atf_check -s eq:0 -o empty -e empty touch d/foo
+ atf_check -s eq:0 -o empty -e empty test -f d/foo
+ atf_check -s eq:0 -o empty -e empty test -f e/foo
+
+ test_unmount
+}
+
+atf_test_case kqueue
+kqueue_head() {
+ atf_set "descr" "Tests that creating a symlink raises the" \
+ "appropriate kqueue events"
+ atf_set "require.user" "root"
+}
+kqueue_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir dir
+ echo 'ln -s non-existent dir/a' | kqueue_monitor 1 dir
+ kqueue_check dir NOTE_WRITE
+ atf_check -s eq:0 -o empty -e empty rm dir/a
+ atf_check -s eq:0 -o empty -e empty rmdir dir
+
+ test_unmount
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case file
+ atf_add_test_case exec
+ atf_add_test_case dir
+ atf_add_test_case kqueue
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_times.sh b/contrib/netbsd-tests/fs/tmpfs/t_times.sh
new file mode 100755
index 000000000000..7b3be8d98d03
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_times.sh
@@ -0,0 +1,169 @@
+# $NetBSD: t_times.sh,v 1.5 2010/11/07 17:51:18 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# Verifies that node times are properly handled.
+#
+
+atf_test_case empty
+empty_head() {
+ atf_set "descr" "Tests that creating an empty file and later" \
+ "manipulating it updates times correctly"
+ atf_set "require.user" "root"
+}
+empty_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty touch a
+ eval $(stat -s a | sed -e 's|st_|ost_|g') || atf_fail "stat failed"
+ [ ${ost_birthtime} -eq ${ost_atime} ] || atf_fail "Incorrect atime"
+ [ ${ost_birthtime} -eq ${ost_ctime} ] || atf_fail "Incorrect ctime"
+ [ ${ost_birthtime} -eq ${ost_mtime} ] || atf_fail "Incorrect mtime"
+
+ sleep 1
+ atf_check -s eq:0 -o empty -e empty cat a
+ eval $(stat -s a) || atf_fail "stat failed"
+ [ ${st_atime} -gt ${ost_atime} ] || atf_fail "Incorrect atime"
+ [ ${st_ctime} -eq ${ost_ctime} ] || atf_fail "Incorrect ctime"
+ [ ${st_mtime} -eq ${ost_mtime} ] || atf_fail "Incorrect mtime"
+
+ sleep 1
+ ost_atime=${st_atime}
+ echo foo >a || atf_fail "Write failed"
+ eval $(stat -s a) || atf_fail "stat failed"
+ [ ${st_atime} -gt ${ost_atime} ] || atf_fail "Incorrect atime"
+ [ ${st_ctime} -gt ${ost_ctime} ] || atf_fail "Incorrect ctime"
+ [ ${st_mtime} -gt ${ost_mtime} ] || atf_fail "Incorrect mtime"
+
+ test_unmount
+}
+
+atf_test_case holey
+holey_head() {
+ atf_set "descr" "Tests that creating a file consisting entirely" \
+ "of a hole and later" \
+ "manipulating it updates times correctly"
+ atf_set "require.user" "root"
+}
+holey_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty truncate -s 8k a
+ eval $(stat -s a | sed -e 's|st_|ost_|g') || atf_fail "stat failed"
+ [ ${ost_birthtime} -eq ${ost_atime} ] || atf_fail "Incorrect atime"
+ [ ${ost_birthtime} -eq ${ost_ctime} ] || atf_fail "Incorrect ctime"
+ [ ${ost_birthtime} -eq ${ost_mtime} ] || atf_fail "Incorrect mtime"
+
+ sleep 1
+ atf_check -s eq:0 -o ignore -e empty cat a
+ eval $(stat -s a) || atf_fail "stat failed"
+ [ ${st_atime} -gt ${ost_atime} ] || atf_fail "Incorrect atime"
+ [ ${st_ctime} -eq ${ost_ctime} ] || atf_fail "Incorrect ctime"
+ [ ${st_mtime} -eq ${ost_mtime} ] || atf_fail "Incorrect mtime"
+
+ test_unmount
+}
+
+atf_test_case non_empty
+non_empty_head() {
+ atf_set "descr" "Tests that creating a non-empty file and later" \
+ "manipulating it updates times correctly"
+ atf_set "require.user" "root"
+}
+non_empty_body() {
+ test_mount
+
+ echo foo >b || atf_fail "Non-empty creation failed"
+ eval $(stat -s b | sed -e 's|st_|ost_|g') || atf_fail "stat failed"
+
+ sleep 1
+ atf_check -s eq:0 -o inline:"foo\n" -e empty cat b
+ eval $(stat -s b) || atf_fail "stat failed"
+ [ ${st_atime} -gt ${ost_atime} ] || atf_fail "Incorrect atime"
+ [ ${st_ctime} -eq ${ost_ctime} ] || atf_fail "Incorrect ctime"
+ [ ${st_mtime} -eq ${ost_mtime} ] || atf_fail "Incorrect mtime"
+
+ test_unmount
+}
+
+atf_test_case link
+link_head() {
+ atf_set "descr" "Tests that linking to an existing file updates" \
+ "times correctly"
+ atf_set "require.user" "root"
+}
+link_body() {
+ test_mount
+
+ echo foo >c || atf_fail "Non-empty creation failed"
+ eval $(stat -s c | sed -e 's|st_|ost_|g') || atf_fail "stat failed"
+
+ sleep 1
+ atf_check -s eq:0 -o empty -e empty ln c d
+ eval $(stat -s c) || atf_fail "stat failed"
+ [ ${st_atime} -eq ${ost_atime} ] || atf_fail "Incorrect atime"
+ [ ${st_ctime} -gt ${ost_ctime} ] || atf_fail "Incorrect ctime"
+ [ ${st_mtime} -eq ${ost_mtime} ] || atf_fail "Incorrect mtime"
+
+ test_unmount
+}
+
+atf_test_case rename
+rename_head() {
+ atf_set "descr" "Tests that renaming an existing file updates" \
+ "times correctly"
+ atf_set "require.user" "root"
+}
+rename_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir e
+ echo foo >e/a || atf_fail "Creation failed"
+ eval $(stat -s e | sed -e 's|st_|dost_|g') || atf_fail "stat failed"
+ eval $(stat -s e/a | sed -e 's|st_|ost_|g') || atf_fail "stat failed"
+ sleep 1
+ atf_check -s eq:0 -o empty -e empty mv e/a e/b
+ eval $(stat -s e | sed -e 's|st_|dst_|g') || atf_fail "stat failed"
+ eval $(stat -s e/b) || atf_fail "stat failed"
+ [ ${st_atime} -eq ${ost_atime} ] || atf_fail "Incorrect atime"
+ [ ${st_ctime} -gt ${ost_ctime} ] || atf_fail "Incorrect ctime"
+ [ ${st_mtime} -eq ${ost_mtime} ] || atf_fail "Incorrect mtime"
+ [ ${dst_mtime} -gt ${dost_mtime} ] || atf_fail "Incorrect mtime"
+
+ test_unmount
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case empty
+ atf_add_test_case holey
+ atf_add_test_case non_empty
+ atf_add_test_case link
+ atf_add_test_case rename
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_trail_slash.sh b/contrib/netbsd-tests/fs/tmpfs/t_trail_slash.sh
new file mode 100755
index 000000000000..df5b023711bb
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_trail_slash.sh
@@ -0,0 +1,52 @@
+# $NetBSD: t_trail_slash.sh,v 1.5 2010/11/07 17:51:18 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+atf_test_case main
+main_head() {
+ atf_set "descr" "Verifies that trailing slashes are not stored" \
+ "in directory names and that they do not cause" \
+ "crashes"
+ atf_set "require.user" "root"
+}
+main_body() {
+ test_mount
+
+ atf_check -s eq:0 -o empty -e empty mkdir a/
+ atf_check -s eq:0 -o empty -e empty touch a/b
+ atf_check -s eq:0 -o empty -e empty test -f a/b
+ atf_check -s eq:0 -o empty -e empty rm a/b
+ atf_check -s eq:0 -o empty -e empty rmdir a/
+
+ test_unmount
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case main
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_truncate.sh b/contrib/netbsd-tests/fs/tmpfs/t_truncate.sh
new file mode 100755
index 000000000000..2bc1902795cb
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_truncate.sh
@@ -0,0 +1,56 @@
+# $NetBSD: t_truncate.sh,v 1.4 2010/11/07 17:51:18 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+atf_test_case basic
+basic_head() {
+ atf_set "descr" "Verifies that the truncate operation works"
+ atf_set "require.user" "root"
+}
+basic_body() {
+ test_mount
+
+ echo "Creating big file"
+ jot 10000 >a || atf_fail "Failed to create big file"
+ echo "Trunctaing the file to a smaller size"
+ echo foo >a || atf_fail "Failed to truncate file to a smaller size"
+ [ $(md5 a | cut -d ' ' -f 4) = d3b07384d113edec49eaa6238ad5ff00 ] || \
+ echo "Truncated file is incorrect"
+
+ echo "Truncating to zero bytes"
+ >a || atf_fail "Failed to truncate to 0"
+ echo "Truncating to zero bytes, second try"
+ >a || atf_fail "Failed to re-truncate to 0"
+
+ test_unmount
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case basic
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_vnd.sh b/contrib/netbsd-tests/fs/tmpfs/t_vnd.sh
new file mode 100755
index 000000000000..0929b55d1852
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_vnd.sh
@@ -0,0 +1,102 @@
+# $NetBSD: t_vnd.sh,v 1.9 2016/07/29 05:23:24 pgoyette Exp $
+#
+# Copyright (c) 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# Verifies that vnd works with files stored in tmpfs.
+#
+
+# Begin FreeBSD
+MD_DEVICE_FILE=md.device
+# End FreeBSD
+
+atf_test_case basic cleanup
+basic_head() {
+ atf_set "descr" "Verifies that vnd works with files stored in tmpfs"
+ atf_set "require.user" "root"
+}
+basic_body() {
+ test_mount
+
+ atf_check -s eq:0 -o ignore -e ignore \
+ dd if=/dev/zero of=disk.img bs=1m count=10
+ # Begin FreeBSD
+ if true; then
+ atf_check -s eq:0 -o empty -e empty mkdir mnt
+ atf_check -s eq:0 -o empty -e empty mdmfs -F disk.img md mnt
+ md_dev=$(df mnt | awk 'NR != 1 { print $1 }' | xargs basename)
+ atf_check test -c /dev/$md_dev # Sanity check
+ echo -n $md_dev > $TMPDIR/$MD_DEVICE_FILE
+ else
+ # End FreeBSD
+ atf_check -s eq:0 -o empty -e empty vndconfig /dev/vnd3 disk.img
+
+ atf_check -s eq:0 -o ignore -e ignore newfs /dev/rvnd3a
+
+ atf_check -s eq:0 -o empty -e empty mkdir mnt
+ atf_check -s eq:0 -o empty -e empty mount /dev/vnd3a mnt
+ # Begin FreeBSD
+ fi
+ # End FreeBSD
+
+ echo "Creating test files"
+ for f in $(jot -w %u 100 | uniq); do
+ jot 1000 >mnt/${f} || atf_fail "Failed to create file ${f}"
+ done
+
+ echo "Verifying created files"
+ for f in $(jot -w %u 100 | uniq); do
+ [ $(md5 mnt/${f} | cut -d ' ' -f 4) = \
+ 53d025127ae99ab79e8502aae2d9bea6 ] || \
+ atf_fail "Invalid checksum for file ${f}"
+ done
+
+ atf_check -s eq:0 -o empty -e empty umount mnt
+ atf_check -s eq:0 -o empty -e empty vndconfig -u /dev/vnd3
+
+ test_unmount
+ touch done
+}
+basic_cleanup() {
+ # Begin FreeBSD
+ if md_dev=$(cat $TMPDIR/$MD_DEVICE_FILE); then
+ echo "Will try disconnecting $md_dev"
+ else
+ echo "$MD_DEVICE_FILE doesn't exist in $TMPDIR; returning early"
+ return 0
+ fi
+ # End FreeBSD
+ if [ ! -f done ]; then
+ umount mnt 2>/dev/null 1>&2
+ vndconfig -u /dev/vnd3 2>/dev/null 1>&2
+ fi
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case basic
+}
diff --git a/contrib/netbsd-tests/fs/tmpfs/t_vnode_leak.sh b/contrib/netbsd-tests/fs/tmpfs/t_vnode_leak.sh
new file mode 100755
index 000000000000..4630a7cd97fb
--- /dev/null
+++ b/contrib/netbsd-tests/fs/tmpfs/t_vnode_leak.sh
@@ -0,0 +1,68 @@
+# $NetBSD: t_vnode_leak.sh,v 1.6 2010/11/07 17:51:18 jmmv Exp $
+#
+# Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+atf_test_case main cleanup
+main_head() {
+ atf_set "descr" "Verifies that vnodes are not leaked and that" \
+ "their reclaim operation works as expected: i.e.," \
+ "when all free vnodes are exhausted, unused ones" \
+ "have to be recycled, which is what the reclaim" \
+ "operation does."
+ atf_set "require.user" "root"
+}
+main_body() {
+ echo "Lowering kern.maxvnodes to 2000"
+ # Begin FreeBSD
+ if true; then
+ sysctl -n kern.maxvnodes > oldvnodes
+ else
+ # End FreeBSD
+ sysctl kern.maxvnodes | awk '{ print $3; }' >oldvnodes
+ # Begin FreeBSD
+ fi
+ # End FreeBSD
+ atf_check -s eq:0 -o ignore -e empty sysctl -w kern.maxvnodes=2000
+
+ test_mount -o -s$(((4000 + 2) * 4096))
+ echo "Creating 4000 directories"
+ for f in $(jot 4000); do
+ mkdir ${f}
+ done
+ test_unmount
+}
+main_cleanup() {
+ oldvnodes=$(cat oldvnodes)
+ echo "Restoring kern.maxvnodes to ${oldvnodes}"
+ sysctl -w kern.maxvnodes=${oldvnodes}
+}
+
+atf_init_test_cases() {
+ . $(atf_get_srcdir)/../h_funcs.subr
+ . $(atf_get_srcdir)/h_funcs.subr
+
+ atf_add_test_case main
+}
diff --git a/contrib/netbsd-tests/fs/umapfs/t_basic.c b/contrib/netbsd-tests/fs/umapfs/t_basic.c
new file mode 100644
index 000000000000..c9b2d9b7a5dc
--- /dev/null
+++ b/contrib/netbsd-tests/fs/umapfs/t_basic.c
@@ -0,0 +1,145 @@
+/* $NetBSD: t_basic.c,v 1.5 2017/01/13 21:30:40 christos Exp $ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <atf-c.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+#include <rump/rumpvfs_if_pub.h>
+
+#include <fs/tmpfs/tmpfs_args.h>
+#include <miscfs/umapfs/umap.h>
+
+#include "h_macros.h"
+
+ATF_TC(basic);
+ATF_TC_HEAD(basic, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "basic umapfs mapping");
+}
+
+static void
+xtouch(const char *path)
+{
+ int fd;
+
+ fd = rump_sys_open(path, O_CREAT | O_RDWR, 0777);
+ if (fd == -1)
+ atf_tc_fail_errno("create %s", path);
+ rump_sys_close(fd);
+}
+
+static void
+xchown(const char *path, uid_t uid, gid_t gid)
+{
+
+ if (rump_sys_chown(path, uid, gid) == -1)
+ atf_tc_fail_errno("chown %s failed", path);
+}
+
+static void
+testuidgid(const char *path, uid_t uid, gid_t gid)
+{
+ struct stat sb;
+
+ if (rump_sys_stat(path, &sb) == -1)
+ atf_tc_fail_errno("stat %s", path);
+ if (uid != (uid_t)-1) {
+ if (sb.st_uid != uid)
+ atf_tc_fail("%s: expected uid %d, got %d",
+ path, uid, sb.st_uid);
+ }
+ if (gid != (gid_t)-1) {
+ if (sb.st_gid != gid)
+ atf_tc_fail("%s: expected gid %d, got %d",
+ path, gid, sb.st_gid);
+ }
+}
+
+ATF_TC_BODY(basic, tc)
+{
+ struct umap_args umargs;
+ struct tmpfs_args targs;
+ u_long umaps[2][2];
+ u_long gmaps[2][2];
+
+ rump_init();
+ if (rump_sys_mkdir("/td1", 0777) == -1)
+ atf_tc_fail_errno("mp1");
+ if (rump_sys_mkdir("/td2", 0777) == -1)
+ atf_tc_fail_errno("mp1");
+
+ /* use tmpfs because rumpfs doesn't support ownership */
+ memset(&targs, 0, sizeof(targs));
+ targs.ta_version = TMPFS_ARGS_VERSION;
+ targs.ta_root_mode = 0777;
+ if (rump_sys_mount(MOUNT_TMPFS, "/td1", 0, &targs, sizeof(targs)) == -1)
+ atf_tc_fail_errno("could not mount tmpfs td1");
+
+ memset(&umargs, 0, sizeof(umargs));
+
+ /*
+ * Map td1 uid 555 to td2 uid 777 (yes, IMHO the umapfs
+ * mapping format is counter-intuitive).
+ */
+ umaps[0][0] = 777;
+ umaps[0][1] = 555;
+ umaps[1][0] = 0;
+ umaps[1][1] = 0;
+ gmaps[0][0] = 4321;
+ gmaps[0][1] = 1234;
+ gmaps[1][0] = 0;
+ gmaps[1][1] = 0;
+
+ umargs.umap_target = __UNCONST("/td1");
+ umargs.nentries = 2;
+ umargs.gnentries = 2;
+ umargs.mapdata = umaps;
+ umargs.gmapdata = gmaps;
+
+ if (rump_sys_mount(MOUNT_UMAP, "/td2", 0, &umargs,sizeof(umargs)) == -1)
+ atf_tc_fail_errno("could not mount umapfs");
+
+ xtouch("/td1/noch");
+ testuidgid("/td1/noch", 0, 0);
+ testuidgid("/td2/noch", 0, 0);
+
+ xtouch("/td1/nomap");
+ xchown("/td1/nomap", 1, 2);
+ testuidgid("/td1/nomap", 1, 2);
+ testuidgid("/td2/nomap", -1, -1);
+
+ xtouch("/td1/forwmap");
+ xchown("/td1/forwmap", 555, 1234);
+ testuidgid("/td1/forwmap", 555, 1234);
+ testuidgid("/td2/forwmap", 777, 4321);
+
+ /*
+ * this *CANNOT* be correct???
+ */
+ xtouch("/td1/revmap");
+ /*
+ * should be 777 / 4321 (?), but makes first test fail since
+ * it gets 777 / 4321, i.e. unmapped results.
+ */
+ xchown("/td2/revmap", 555, 1234);
+ testuidgid("/td1/revmap", 555, 1234);
+ testuidgid("/td2/revmap", 777, 4321);
+
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, basic);
+ return 0; /*XXX?*/
+}
diff --git a/contrib/netbsd-tests/fs/union/t_pr.c b/contrib/netbsd-tests/fs/union/t_pr.c
new file mode 100644
index 000000000000..fc70bcadf36b
--- /dev/null
+++ b/contrib/netbsd-tests/fs/union/t_pr.c
@@ -0,0 +1,130 @@
+/* $NetBSD: t_pr.c,v 1.9 2017/01/13 21:30:40 christos Exp $ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <atf-c.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include <miscfs/union/union.h>
+
+#include "h_macros.h"
+
+ATF_TC(multilayer);
+ATF_TC_HEAD(multilayer, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "mount_union -b twice");
+}
+
+ATF_TC_BODY(multilayer, tc)
+{
+ struct union_args unionargs;
+
+ rump_init();
+
+ if (rump_sys_mkdir("/Tunion", 0777) == -1)
+ atf_tc_fail_errno("mkdir mp1");
+ if (rump_sys_mkdir("/Tunion2", 0777) == -1)
+ atf_tc_fail_errno("mkdir mp2");
+ if (rump_sys_mkdir("/Tunion2/A", 0777) == -1)
+ atf_tc_fail_errno("mkdir A");
+ if (rump_sys_mkdir("/Tunion2/B", 0777) == -1)
+ atf_tc_fail_errno("mkdir B");
+
+ unionargs.target = __UNCONST("/Tunion2/A");
+ unionargs.mntflags = UNMNT_BELOW;
+
+ if (rump_sys_mount(MOUNT_UNION, "/Tunion", 0,
+ &unionargs, sizeof(unionargs)) == -1)
+ atf_tc_fail_errno("union mount");
+
+ unionargs.target = __UNCONST("/Tunion2/B");
+ unionargs.mntflags = UNMNT_BELOW;
+
+ rump_sys_mount(MOUNT_UNION, "/Tunion", 0,&unionargs,sizeof(unionargs));
+}
+
+ATF_TC(devnull1);
+ATF_TC_HEAD(devnull1, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "mount_union -b and "
+ "'echo x > /un/null'");
+}
+
+ATF_TC_BODY(devnull1, tc)
+{
+ struct union_args unionargs;
+ int fd, res;
+
+ rump_init();
+
+ if (rump_sys_mkdir("/mp", 0777) == -1)
+ atf_tc_fail_errno("mkdir mp");
+
+ unionargs.target = __UNCONST("/dev");
+ unionargs.mntflags = UNMNT_BELOW;
+
+ if (rump_sys_mount(MOUNT_UNION, "/mp", 0,
+ &unionargs, sizeof(unionargs)) == -1)
+ atf_tc_fail_errno("union mount");
+
+ fd = rump_sys_open("/mp/null", O_WRONLY | O_CREAT | O_TRUNC);
+
+ if (fd == -1)
+ atf_tc_fail_errno("open");
+
+ res = rump_sys_write(fd, &fd, sizeof(fd));
+ if (res != sizeof(fd))
+ atf_tc_fail("write");
+}
+
+ATF_TC(devnull2);
+ATF_TC_HEAD(devnull2, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "mount_union -b and "
+ "'echo x >> /un/null'");
+}
+
+ATF_TC_BODY(devnull2, tc)
+{
+ struct union_args unionargs;
+ int fd, res;
+
+ rump_init();
+
+ if (rump_sys_mkdir("/mp", 0777) == -1)
+ atf_tc_fail_errno("mkdir mp");
+
+ unionargs.target = __UNCONST("/dev");
+ unionargs.mntflags = UNMNT_BELOW;
+
+ if (rump_sys_mount(MOUNT_UNION, "/mp", 0,
+ &unionargs, sizeof(unionargs)) == -1)
+ atf_tc_fail_errno("union mount");
+
+ fd = rump_sys_open("/mp/null", O_WRONLY | O_CREAT | O_APPEND);
+ if (fd == -1)
+ atf_tc_fail_errno("open");
+
+ res = rump_sys_write(fd, &fd, sizeof(fd));
+ if (res != sizeof(fd))
+ atf_tc_fail("write");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, multilayer);
+ ATF_TP_ADD_TC(tp, devnull1);
+ ATF_TP_ADD_TC(tp, devnull2);
+
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/vfs/t_full.c b/contrib/netbsd-tests/fs/vfs/t_full.c
new file mode 100644
index 000000000000..51347deca70d
--- /dev/null
+++ b/contrib/netbsd-tests/fs/vfs/t_full.c
@@ -0,0 +1,101 @@
+/* $NetBSD: t_full.c,v 1.9 2017/01/13 21:30:40 christos Exp $ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <rump/rump_syscalls.h>
+#include <rump/rump.h>
+
+#include "../common/h_fsmacros.h"
+#include "h_macros.h"
+
+/*
+ * Write this much over the image size. This is to force an NFS commit,
+ * since we might just stuff data into the cache and miss the problem.
+ */
+#define NFSBONUS (1<<16)
+
+static void
+fillfs(const atf_tc_t *tc, const char *mp)
+{
+ char buf[8192];
+ size_t written;
+ ssize_t n = 0; /* xxxgcc */
+ size_t bonus;
+ int fd, i = 0;
+
+ if (FSTYPE_P2K_FFS(tc) || FSTYPE_PUFFS(tc) || FSTYPE_RUMPFS(tc)) {
+ atf_tc_skip("fs does not support explicit block allocation "
+ "(GOP_ALLOC)");
+ }
+
+ bonus = 0;
+ if (FSTYPE_NFS(tc))
+ bonus = NFSBONUS;
+
+ if (rump_sys_chdir(mp) == -1)
+ atf_tc_fail_errno("chdir mountpoint");
+ fd = rump_sys_open("afile", O_CREAT | O_RDWR);
+ if (fd == -1)
+ atf_tc_fail_errno("create file");
+
+ for (written = 0; written < FSTEST_IMGSIZE + bonus; written +=n) {
+ memset(buf, i++, sizeof(buf)); /* known garbage */
+ n = rump_sys_write(fd, buf, sizeof(buf));
+ if (n == -1)
+ break;
+ }
+ if (FSTYPE_ZFS(tc))
+ atf_tc_expect_fail("PR kern/47656: Test known to be broken");
+ if (n == -1) {
+ if (errno != ENOSPC)
+ atf_tc_fail_errno("write");
+ } else {
+ atf_tc_fail("filled file system over size limit");
+ }
+
+ rump_sys_close(fd);
+ rump_sys_chdir("/");
+}
+
+ATF_TC_FSAPPLY(fillfs, "fills file system, expects ENOSPC");
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_FSAPPLY(fillfs);
+
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/vfs/t_io.c b/contrib/netbsd-tests/fs/vfs/t_io.c
new file mode 100644
index 000000000000..b7f652936e10
--- /dev/null
+++ b/contrib/netbsd-tests/fs/vfs/t_io.c
@@ -0,0 +1,268 @@
+/* $NetBSD: t_io.c,v 1.17 2017/01/13 21:30:40 christos Exp $ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <rump/rump_syscalls.h>
+#include <rump/rump.h>
+
+#include "../common/h_fsmacros.h"
+#include "h_macros.h"
+
+#define TESTSTR "this is a string. collect enough and you'll have Em"
+#define TESTSZ sizeof(TESTSTR)
+
+static void
+holywrite(const atf_tc_t *tc, const char *mp)
+{
+ char buf[1024];
+ char *b2, *b3;
+ size_t therange = getpagesize()+1;
+ int fd;
+
+ FSTEST_ENTER();
+
+ RL(fd = rump_sys_open("file", O_RDWR|O_CREAT|O_TRUNC, 0666));
+
+ memset(buf, 'A', sizeof(buf));
+ RL(rump_sys_pwrite(fd, buf, 1, getpagesize()));
+
+ memset(buf, 'B', sizeof(buf));
+ RL(rump_sys_pwrite(fd, buf, 2, getpagesize()-1));
+
+ REQUIRE_LIBC(b2 = malloc(2 * getpagesize()), NULL);
+ REQUIRE_LIBC(b3 = malloc(2 * getpagesize()), NULL);
+
+ RL(rump_sys_pread(fd, b2, therange, 0));
+
+ memset(b3, 0, therange);
+ memset(b3 + getpagesize() - 1, 'B', 2);
+
+ ATF_REQUIRE_EQ(memcmp(b2, b3, therange), 0);
+
+ rump_sys_close(fd);
+ FSTEST_EXIT();
+}
+
+static void
+extendbody(const atf_tc_t *tc, off_t seekcnt)
+{
+ char buf[TESTSZ+1];
+ struct stat sb;
+ int fd;
+
+ FSTEST_ENTER();
+ RL(fd = rump_sys_open("testfile",
+ O_CREAT | O_RDWR | (seekcnt ? O_APPEND : 0)));
+ RL(rump_sys_ftruncate(fd, seekcnt));
+ RL(rump_sys_fstat(fd, &sb));
+ ATF_REQUIRE_EQ(sb.st_size, seekcnt);
+
+ ATF_REQUIRE_EQ(rump_sys_write(fd, TESTSTR, TESTSZ), TESTSZ);
+ ATF_REQUIRE_EQ(rump_sys_pread(fd, buf, TESTSZ, seekcnt), TESTSZ);
+ ATF_REQUIRE_STREQ(buf, TESTSTR);
+
+ RL(rump_sys_fstat(fd, &sb));
+ ATF_REQUIRE_EQ(sb.st_size, (off_t)TESTSZ + seekcnt);
+ RL(rump_sys_close(fd));
+ FSTEST_EXIT();
+}
+
+static void
+extendfile(const atf_tc_t *tc, const char *mp)
+{
+
+ extendbody(tc, 0);
+}
+
+static void
+extendfile_append(const atf_tc_t *tc, const char *mp)
+{
+
+ extendbody(tc, 37);
+}
+
+static void
+overwritebody(const atf_tc_t *tc, off_t count, bool dotrunc)
+{
+ char *buf;
+ int fd;
+
+ REQUIRE_LIBC(buf = malloc(count), NULL);
+ FSTEST_ENTER();
+ RL(fd = rump_sys_open("testi", O_CREAT | O_RDWR, 0666));
+ ATF_REQUIRE_EQ(rump_sys_write(fd, buf, count), count);
+ RL(rump_sys_close(fd));
+
+ RL(fd = rump_sys_open("testi", O_RDWR));
+ if (dotrunc)
+ RL(rump_sys_ftruncate(fd, 0));
+ ATF_REQUIRE_EQ(rump_sys_write(fd, buf, count), count);
+ RL(rump_sys_close(fd));
+ FSTEST_EXIT();
+}
+
+static void
+overwrite512(const atf_tc_t *tc, const char *mp)
+{
+
+ overwritebody(tc, 512, false);
+}
+
+static void
+overwrite64k(const atf_tc_t *tc, const char *mp)
+{
+
+ overwritebody(tc, 1<<16, false);
+}
+
+static void
+overwrite_trunc(const atf_tc_t *tc, const char *mp)
+{
+
+ overwritebody(tc, 1<<16, true);
+}
+
+static void
+shrinkfile(const atf_tc_t *tc, const char *mp)
+{
+ int fd;
+
+ FSTEST_ENTER();
+ RL(fd = rump_sys_open("file", O_RDWR|O_CREAT|O_TRUNC, 0666));
+ RL(rump_sys_ftruncate(fd, 2));
+ RL(rump_sys_ftruncate(fd, 1));
+ rump_sys_close(fd);
+ FSTEST_EXIT();
+}
+
+#define TBSIZE 9000
+static void
+read_after_unlink(const atf_tc_t *tc, const char *mp)
+{
+ char buf[TBSIZE], buf2[TBSIZE];
+ int fd;
+
+ FSTEST_ENTER();
+
+ /* create file and put some content into it */
+ RL(fd = rump_sys_open("file", O_RDWR|O_CREAT, 0666));
+ memset(buf, 'D', TBSIZE);
+ ATF_REQUIRE_EQ(rump_sys_write(fd, buf, TBSIZE), TBSIZE);
+ rump_sys_close(fd);
+
+ /* flush buffers from UBC to file system */
+ ATF_REQUIRE_ERRNO(EBUSY, rump_sys_unmount(mp, 0) == -1);
+
+ RL(fd = rump_sys_open("file", O_RDWR));
+ RL(rump_sys_unlink("file"));
+
+ ATF_REQUIRE_EQ(rump_sys_read(fd, buf2, TBSIZE), TBSIZE);
+ ATF_REQUIRE_EQ(memcmp(buf, buf2, TBSIZE), 0);
+ rump_sys_close(fd);
+
+ FSTEST_EXIT();
+}
+
+static void
+wrrd_after_unlink(const atf_tc_t *tc, const char *mp)
+{
+ int value = 0x11;
+ int v2;
+ int fd;
+
+ FSTEST_ENTER();
+
+ RL(fd = rump_sys_open("file", O_RDWR|O_CREAT, 0666));
+ RL(rump_sys_unlink("file"));
+
+ RL(rump_sys_pwrite(fd, &value, sizeof(value), 654321));
+
+ /*
+ * We can't easily invalidate the buffer since we hold a
+ * reference, but try to get them to flush anyway.
+ */
+ RL(rump_sys_fsync(fd));
+ RL(rump_sys_pread(fd, &v2, sizeof(v2), 654321));
+ rump_sys_close(fd);
+
+ ATF_REQUIRE_EQ(value, v2);
+ FSTEST_EXIT();
+}
+
+static void
+read_fault(const atf_tc_t *tc, const char *mp)
+{
+ char ch = 123;
+ int fd;
+
+ FSTEST_ENTER();
+ RL(fd = rump_sys_open("file", O_CREAT | O_RDWR, 0777));
+ ATF_REQUIRE_EQ(rump_sys_write(fd, &ch, 1), 1);
+ RL(rump_sys_close(fd));
+ RL(fd = rump_sys_open("file", O_RDONLY | O_SYNC | O_RSYNC));
+ ATF_REQUIRE_ERRNO(EFAULT, rump_sys_read(fd, NULL, 1) == -1);
+ RL(rump_sys_close(fd));
+ FSTEST_EXIT();
+}
+
+ATF_TC_FSAPPLY(holywrite, "create a sparse file and fill hole");
+ATF_TC_FSAPPLY(extendfile, "check that extending a file works");
+ATF_TC_FSAPPLY(extendfile_append, "check that extending a file works "
+ "with a append-only fd (PR kern/44307)");
+ATF_TC_FSAPPLY(overwrite512, "write a 512 byte file twice");
+ATF_TC_FSAPPLY(overwrite64k, "write a 64k byte file twice");
+ATF_TC_FSAPPLY(overwrite_trunc, "write 64k + truncate + rewrite");
+ATF_TC_FSAPPLY(shrinkfile, "shrink file");
+ATF_TC_FSAPPLY(read_after_unlink, "contents can be read off disk after unlink");
+ATF_TC_FSAPPLY(wrrd_after_unlink, "file can be written and read after unlink");
+ATF_TC_FSAPPLY(read_fault, "read at bad address must return EFAULT");
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_FSAPPLY(holywrite);
+ ATF_TP_FSAPPLY(extendfile);
+ ATF_TP_FSAPPLY(extendfile_append);
+ ATF_TP_FSAPPLY(overwrite512);
+ ATF_TP_FSAPPLY(overwrite64k);
+ ATF_TP_FSAPPLY(overwrite_trunc);
+ ATF_TP_FSAPPLY(shrinkfile);
+ ATF_TP_FSAPPLY(read_after_unlink);
+ ATF_TP_FSAPPLY(wrrd_after_unlink);
+ ATF_TP_FSAPPLY(read_fault);
+
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/vfs/t_mtime_otrunc.c b/contrib/netbsd-tests/fs/vfs/t_mtime_otrunc.c
new file mode 100644
index 000000000000..bb0760992ad2
--- /dev/null
+++ b/contrib/netbsd-tests/fs/vfs/t_mtime_otrunc.c
@@ -0,0 +1,86 @@
+/*-
+ * Copyright (c) 2017 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__COPYRIGHT("@(#) Copyright (c) 2013\
+ The NetBSD Foundation, inc. All rights reserved.");
+__RCSID("$NetBSD: t_mtime_otrunc.c,v 1.1 2017/02/02 22:07:05 martin Exp $");
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <atf-c.h>
+
+#include <rump/rump_syscalls.h>
+#include <rump/rump.h>
+
+#include "../common/h_fsmacros.h"
+
+#define LOCKFILE "lock"
+
+static time_t
+lock_it(void)
+{
+ struct stat st;
+
+ if (rump_sys_stat(LOCKFILE, &st) != 0)
+ st.st_mtime = 0;
+
+ int f = rump_sys_open(LOCKFILE, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ if (f == -1) return 0;
+ rump_sys_close(f);
+
+ return st.st_mtime;
+}
+
+static void
+otrunc_mtime_update(const atf_tc_t *tc, const char *path)
+{
+ time_t last_ts = 0;
+ int res;
+
+ /* atf_tc_expect_fail("PR kern/51762"); */
+
+ res = rump_sys_chdir(path);
+ if (res == -1)
+ atf_tc_fail("chdir failed");
+
+ for (int i = 0; i < 5; i++) {
+ time_t l = lock_it();
+ printf("last lock: %ld\n", (long)l);
+ ATF_REQUIRE_MSG(i == 0 || l > last_ts,
+ "iteration %d: lock time did not increase, old time %lu, "
+ "new time %lu", i,
+ (unsigned long)last_ts, (unsigned long)l);
+ last_ts = l;
+ sleep(2);
+ }
+ rump_sys_chdir("/");
+}
+
+ATF_FSAPPLY(otrunc_mtime_update, "Checks for mtime updates by open(O_TRUNC) (PR kern/51762)");
diff --git a/contrib/netbsd-tests/fs/vfs/t_renamerace.c b/contrib/netbsd-tests/fs/vfs/t_renamerace.c
new file mode 100644
index 000000000000..63cfb4479c89
--- /dev/null
+++ b/contrib/netbsd-tests/fs/vfs/t_renamerace.c
@@ -0,0 +1,190 @@
+/* $NetBSD: t_renamerace.c,v 1.34 2017/01/13 21:30:40 christos Exp $ */
+
+/*
+ * Modified for rump and atf from a program supplied
+ * by Nicolas Joly in kern/40948
+ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/utsname.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+/* Bump the size of the test file system image to a larger value.
+ *
+ * These tests cause a lot of churn in the file system by creating and
+ * deleting files/directories in quick succession. A faster CPU will cause
+ * more churn because the tests are capped by a run time period in seconds,
+ * not number of operations.
+ *
+ * This is all fine except for LFS, because the lfs_cleanerd cannot keep up
+ * with the churn and thus causes the test to fail on fast machines. Hence
+ * the reason for this hack. */
+#define FSTEST_IMGSIZE (50000 * 512)
+
+#include "../common/h_fsmacros.h"
+#include "h_macros.h"
+
+static volatile int quittingtime;
+pid_t wrkpid;
+
+static void *
+w1(void *arg)
+{
+ int fd;
+
+ rump_pub_lwproc_newlwp(wrkpid);
+
+ while (!quittingtime) {
+ fd = rump_sys_open("rename.test1",
+ O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ if (fd == -1 && errno != EEXIST)
+ atf_tc_fail_errno("create");
+ rump_sys_unlink("rename.test1");
+ rump_sys_close(fd);
+ }
+
+ return NULL;
+}
+
+static void *
+w1_dirs(void *arg)
+{
+
+ rump_pub_lwproc_newlwp(wrkpid);
+
+ while (!quittingtime) {
+ if (rump_sys_mkdir("rename.test1", 0777) == -1)
+ atf_tc_fail_errno("mkdir");
+ rump_sys_rmdir("rename.test1");
+ }
+
+ return NULL;
+}
+
+static void *
+w2(void *arg)
+{
+
+ rump_pub_lwproc_newlwp(wrkpid);
+
+ while (!quittingtime) {
+ rump_sys_rename("rename.test1", "rename.test2");
+ }
+
+ return NULL;
+}
+
+#define NWRK 8
+static void
+renamerace(const atf_tc_t *tc, const char *mp)
+{
+ pthread_t pt1[NWRK], pt2[NWRK];
+ int i;
+
+ /*
+ * Sysvbfs supports only 8 inodes so this test would exhaust
+ * the inode table and creating files would fail with ENOSPC.
+ */
+ if (FSTYPE_SYSVBFS(tc))
+ atf_tc_skip("filesystem has not enough inodes");
+ if (FSTYPE_RUMPFS(tc))
+ atf_tc_skip("rename not supported by file system");
+ if (FSTYPE_UDF(tc))
+ atf_tc_expect_fail("PR kern/49046");
+
+ RZ(rump_pub_lwproc_rfork(RUMP_RFCFDG));
+ RL(wrkpid = rump_sys_getpid());
+
+ RL(rump_sys_chdir(mp));
+ for (i = 0; i < NWRK; i++)
+ pthread_create(&pt1[i], NULL, w1, NULL);
+
+ for (i = 0; i < NWRK; i++)
+ pthread_create(&pt2[i], NULL, w2, NULL);
+
+ sleep(5);
+ quittingtime = 1;
+
+ for (i = 0; i < NWRK; i++)
+ pthread_join(pt1[i], NULL);
+ for (i = 0; i < NWRK; i++)
+ pthread_join(pt2[i], NULL);
+ RL(rump_sys_chdir("/"));
+
+ if (FSTYPE_UDF(tc))
+ atf_tc_fail("race did not trigger this time");
+
+ if (FSTYPE_MSDOS(tc)) {
+ atf_tc_expect_fail("PR kern/43626");
+ /*
+ * XXX: race does not trigger every time at least
+ * on amd64/qemu.
+ */
+ if (msdosfs_fstest_unmount(tc, mp, 0) != 0) {
+ rump_pub_vfs_mount_print(mp, 1);
+ atf_tc_fail_errno("unmount failed");
+ }
+ atf_tc_fail("race did not trigger this time");
+ }
+}
+
+static void
+renamerace_dirs(const atf_tc_t *tc, const char *mp)
+{
+ pthread_t pt1, pt2;
+
+ if (FSTYPE_SYSVBFS(tc))
+ atf_tc_skip("directories not supported by file system");
+
+ if (FSTYPE_RUMPFS(tc))
+ atf_tc_skip("rename not supported by file system");
+
+ /* XXX: msdosfs also sometimes hangs */
+ if (FSTYPE_MSDOS(tc))
+ atf_tc_expect_signal(-1, "PR kern/43626");
+
+ RZ(rump_pub_lwproc_rfork(RUMP_RFCFDG));
+ RL(wrkpid = rump_sys_getpid());
+
+ RL(rump_sys_chdir(mp));
+ pthread_create(&pt1, NULL, w1_dirs, NULL);
+ pthread_create(&pt2, NULL, w2, NULL);
+
+ sleep(5);
+ quittingtime = 1;
+
+ pthread_join(pt1, NULL);
+ pthread_join(pt2, NULL);
+ RL(rump_sys_chdir("/"));
+
+ /*
+ * Doesn't always trigger when run on a slow backend
+ * (i.e. not on tmpfs/mfs). So do the usual kludge.
+ */
+ if (FSTYPE_MSDOS(tc))
+ abort();
+}
+
+ATF_TC_FSAPPLY(renamerace, "rename(2) race with file unlinked mid-operation");
+ATF_TC_FSAPPLY(renamerace_dirs, "rename(2) race with directories");
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_FSAPPLY(renamerace); /* PR kern/41128 */
+ ATF_TP_FSAPPLY(renamerace_dirs);
+
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/vfs/t_rmdirrace.c b/contrib/netbsd-tests/fs/vfs/t_rmdirrace.c
new file mode 100644
index 000000000000..839dbad73352
--- /dev/null
+++ b/contrib/netbsd-tests/fs/vfs/t_rmdirrace.c
@@ -0,0 +1,106 @@
+/* $NetBSD: t_rmdirrace.c,v 1.9 2012/02/16 02:47:56 perseant Exp $ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nicolas Joly.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <rump/rump_syscalls.h>
+#include <rump/rump.h>
+
+#include "../common/h_fsmacros.h"
+
+#define DIRNAME "rmdir.test"
+
+static void *func1(void *arg)
+{
+
+ while (*(int *)arg != 1)
+ rump_sys_mkdir(DIRNAME, 0755);
+
+ return NULL;
+}
+
+static void *func2(void *arg)
+{
+
+ while (*(int *)arg != 1)
+ rump_sys_rmdir(DIRNAME);
+
+ return NULL;
+}
+
+static void
+race(const atf_tc_t *tc, const char *path)
+{
+ int res, fd, quit;
+ pthread_t th1, th2;
+
+ if (FSTYPE_SYSVBFS(tc))
+ atf_tc_skip("directories not supported by file system");
+
+ fd = rump_sys_open(".", O_RDONLY, 0666);
+ if (fd == -1)
+ atf_tc_fail("open failed");
+ res = rump_sys_chdir(path);
+ if (res == -1)
+ atf_tc_fail("chdir failed");
+
+ quit = 0;
+
+ res = pthread_create(&th1, NULL, func1, &quit);
+ if (res != 0)
+ atf_tc_fail("pthread_create1 failed");
+ res = pthread_create(&th2, NULL, func2, &quit);
+ if (res != 0)
+ atf_tc_fail("pthread_create2 failed");
+
+ sleep(10);
+
+ quit = 1;
+
+ res = pthread_join(th2, NULL);
+ if (res != 0)
+ atf_tc_fail("pthread_join2 failed");
+ res = pthread_join(th1, NULL);
+ if (res != 0)
+ atf_tc_fail("pthread_join1 failed");
+
+ res = rump_sys_fchdir(fd);
+ if (res == -1)
+ atf_tc_fail("fchdir failed");
+}
+
+ATF_FSAPPLY(race, "rmdir(2) race");
diff --git a/contrib/netbsd-tests/fs/vfs/t_ro.c b/contrib/netbsd-tests/fs/vfs/t_ro.c
new file mode 100644
index 000000000000..f7f45813e7b7
--- /dev/null
+++ b/contrib/netbsd-tests/fs/vfs/t_ro.c
@@ -0,0 +1,203 @@
+/* $NetBSD: t_ro.c,v 1.6 2017/01/13 21:30:40 christos Exp $ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <rump/rump_syscalls.h>
+#include <rump/rump.h>
+
+#include "../common/h_fsmacros.h"
+#include "h_macros.h"
+
+#define AFILE "testfile"
+#define ADIR "testdir"
+#define AFIFO "testfifo"
+#define ASYMLINK "testsymlink"
+#define ALINK "testlink"
+#define FUNTEXT "this is some non-humppa text"
+#define FUNSIZE (sizeof(FUNTEXT)-1)
+
+static void
+nullgen(const atf_tc_t *tc, const char *mp)
+{
+
+ return;
+}
+
+static void
+filegen(const atf_tc_t *tc, const char *mp)
+{
+ int fd;
+
+ FSTEST_ENTER();
+ RL(fd = rump_sys_open(AFILE, O_CREAT | O_RDWR, 0777));
+ ATF_REQUIRE_EQ(rump_sys_write(fd, FUNTEXT, FUNSIZE), FUNSIZE);
+ RL(rump_sys_close(fd));
+ FSTEST_EXIT();
+}
+
+/*
+ *
+ * BEGIN tests
+ *
+ */
+
+static void
+create(const atf_tc_t *tc, const char *mp)
+{
+
+ FSTEST_ENTER();
+ ATF_REQUIRE_ERRNO(EROFS, rump_sys_open(AFILE, O_CREAT|O_RDONLY) == -1);
+ FSTEST_EXIT();
+}
+
+static void
+rmfile(const atf_tc_t *tc, const char *mp)
+{
+
+ FSTEST_ENTER();
+ ATF_REQUIRE_ERRNO(EROFS, rump_sys_unlink(AFILE) == -1);
+ FSTEST_EXIT();
+}
+
+static void
+fileio(const atf_tc_t *tc, const char *mp)
+{
+ int fd;
+ char buf[FUNSIZE+1];
+ int expected;
+
+ if (FSTYPE_NFSRO(tc))
+ expected = EACCES;
+ else
+ expected = EROFS;
+
+ FSTEST_ENTER();
+ RL(fd = rump_sys_open(AFILE, O_RDONLY));
+ ATF_REQUIRE_EQ(rump_sys_read(fd, buf, FUNSIZE), FUNSIZE);
+ buf[FUNSIZE] = '\0';
+ ATF_REQUIRE_STREQ(buf, FUNTEXT);
+ RL(rump_sys_close(fd));
+
+ ATF_REQUIRE_ERRNO(expected, rump_sys_open(AFILE, O_WRONLY) == -1);
+ ATF_REQUIRE_ERRNO(expected, rump_sys_open(AFILE, O_RDWR) == -1);
+ FSTEST_EXIT();
+}
+
+static void
+attrs(const atf_tc_t *tc, const char *mp)
+{
+ struct timeval sometvs[2];
+ struct stat sb;
+ int fd;
+
+ FSTEST_ENTER();
+
+ RL(rump_sys_stat(AFILE, &sb));
+
+ ATF_REQUIRE_ERRNO(EROFS, rump_sys_chmod(AFILE, 0775) == -1);
+ if (!FSTYPE_MSDOS(tc))
+ ATF_REQUIRE_ERRNO(EROFS, rump_sys_chown(AFILE, 1, 1) == -1);
+ ATF_REQUIRE_ERRNO(EROFS, rump_sys_utimes(AFILE, sometvs) == -1);
+
+ RL(fd = rump_sys_open(AFILE, O_RDONLY));
+ ATF_REQUIRE_ERRNO(EROFS, rump_sys_fchmod(fd, 0775) == -1);
+ if (!FSTYPE_MSDOS(tc))
+ ATF_REQUIRE_ERRNO(EROFS, rump_sys_fchown(fd, 1, 1) == -1);
+ ATF_REQUIRE_ERRNO(EROFS, rump_sys_futimes(fd, sometvs) == -1);
+ RL(rump_sys_close(fd));
+
+ FSTEST_EXIT();
+}
+
+static void
+createdir(const atf_tc_t *tc, const char *mp)
+{
+
+ FSTEST_ENTER();
+ ATF_REQUIRE_ERRNO(EROFS, rump_sys_mkdir(ADIR, 0775) == -1);
+ FSTEST_EXIT();
+}
+
+static void
+createfifo(const atf_tc_t *tc, const char *mp)
+{
+
+ FSTEST_ENTER();
+ ATF_REQUIRE_ERRNO(EROFS, rump_sys_mkfifo(AFIFO, 0775) == -1);
+ FSTEST_EXIT();
+}
+
+static void
+createsymlink(const atf_tc_t *tc, const char *mp)
+{
+
+ FSTEST_ENTER();
+ ATF_REQUIRE_ERRNO(EROFS, rump_sys_symlink("hoge", ASYMLINK) == -1);
+ FSTEST_EXIT();
+}
+
+static void
+createlink(const atf_tc_t *tc, const char *mp)
+{
+
+ FSTEST_ENTER();
+ ATF_REQUIRE_ERRNO(EROFS, rump_sys_link(AFILE, ALINK) == -1);
+ FSTEST_EXIT();
+}
+
+ATF_TC_FSAPPLY_RO(create, "create file on r/o mount", nullgen);
+ATF_TC_FSAPPLY_RO(rmfile, "remove file from r/o mount", filegen);
+ATF_TC_FSAPPLY_RO(fileio, "can read a file but not write it", filegen);
+ATF_TC_FSAPPLY_RO(attrs, "can query but not change attributes", filegen);
+ATF_TC_FSAPPLY_RO(createdir, "create directory on r/o mount", nullgen);
+ATF_TC_FSAPPLY_RO(createfifo, "create fifo on r/o mount", nullgen);
+ATF_TC_FSAPPLY_RO(createsymlink, "create symlink on r/o mount", nullgen);
+ATF_TC_FSAPPLY_RO(createlink, "create hardlink on r/o mount", filegen);
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_FSAPPLY_RO(create);
+ ATF_TP_FSAPPLY_RO(rmfile);
+ ATF_TP_FSAPPLY_RO(fileio);
+ ATF_TP_FSAPPLY_RO(attrs);
+ ATF_TP_FSAPPLY_RO(createdir);
+ ATF_TP_FSAPPLY_RO(createfifo);
+ ATF_TP_FSAPPLY_RO(createsymlink);
+ ATF_TP_FSAPPLY_RO(createlink);
+
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/vfs/t_rwtoro.c b/contrib/netbsd-tests/fs/vfs/t_rwtoro.c
new file mode 100644
index 000000000000..ca8af07a0a45
--- /dev/null
+++ b/contrib/netbsd-tests/fs/vfs/t_rwtoro.c
@@ -0,0 +1,233 @@
+/* $NetBSD: t_rwtoro.c,v 1.1 2017/01/27 10:45:11 hannken Exp $ */
+
+/*-
+ * Copyright (c) 2017 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <rump/rump_syscalls.h>
+#include <rump/rump.h>
+
+#include <miscfs/nullfs/null.h>
+#include <fs/tmpfs/tmpfs_args.h>
+
+#include "../common/h_fsmacros.h"
+#include "../../h_macros.h"
+
+static const char *unsupported = "fs does not support r/o remount";
+static char file_path[MAXPATHLEN];
+static int file_fd;
+
+/*
+ * Remount the filesystem read-only and test errno.
+ * Skip filesystems that don't implement read-write -> read-only.
+ */
+static void
+remount_ro(const atf_tc_t *tc, const char *mp, int expected_errno)
+{
+ int error;
+ union {
+ struct tmpfs_args tmpfs;
+ char data[4095];
+ } mount_args;
+ int mount_args_length;
+ struct statvfs sbuf;
+
+ if (FSTYPE_ZFS(tc))
+ atf_tc_skip("%s", unsupported);
+
+ /* Prepare mount arguments. */
+ RL(rump_sys_statvfs1(mp, &sbuf, ST_WAIT));
+ mount_args_length = sizeof(mount_args);
+ memset(&mount_args, 0, mount_args_length);
+ if (FSTYPE_TMPFS(tc))
+ mount_args.tmpfs.ta_version = TMPFS_ARGS_VERSION;
+ mount_args_length = rump_sys_mount(sbuf.f_fstypename, mp, MNT_GETARGS,
+ &mount_args, mount_args_length);
+ ATF_CHECK(mount_args_length >= 0);
+
+ /* Remount and test result. */
+ error = rump_sys_mount(sbuf.f_fstypename, mp, MNT_UPDATE | MNT_RDONLY,
+ &mount_args, mount_args_length);
+ if (errno == EOPNOTSUPP)
+ atf_tc_skip("%s", unsupported);
+ if (expected_errno == 0)
+ ATF_CHECK(error == 0);
+ else
+ ATF_CHECK_ERRNO(expected_errno, error == -1);
+}
+
+static void
+open_file_ro(const char *prefix)
+{
+
+ snprintf(file_path, sizeof(file_path), "%s/file", prefix);
+ RL(file_fd = rump_sys_open(file_path, O_CREAT | O_RDWR, 0777));
+ RL(rump_sys_close(file_fd));
+ RL(file_fd = rump_sys_open(file_path, O_RDONLY));
+}
+
+static void
+open_file_ro_unlink(const char *prefix)
+{
+
+ snprintf(file_path, sizeof(file_path), "%s/file", prefix);
+ RL(file_fd = rump_sys_open(file_path, O_CREAT | O_RDWR, 0777));
+ RL(rump_sys_close(file_fd));
+ RL(file_fd = rump_sys_open(file_path, O_RDONLY));
+ RL(rump_sys_unlink(file_path));
+}
+
+static void
+open_file_rw(const char *prefix)
+{
+
+ snprintf(file_path, sizeof(file_path), "%s/file", prefix);
+ RL(file_fd = rump_sys_open(file_path, O_CREAT | O_RDWR, 0777));
+}
+
+static void
+close_file(const char *unused)
+{
+
+ RL(rump_sys_close(file_fd));
+}
+
+static void
+basic_test(const atf_tc_t *tc, const char *mp, int expected_errno,
+ bool use_layer, void (*pre)(const char *), void (*post)(const char *))
+{
+ const char *null_mount = "/nullm";
+ struct null_args nargs;
+
+ if (use_layer) {
+ RL(rump_sys_mkdir(null_mount, 0777));
+ memset(&nargs, 0, sizeof(nargs));
+ nargs.nulla_target = __UNCONST(mp);;
+ RL(rump_sys_mount(MOUNT_NULL, null_mount, 0,
+ &nargs, sizeof(nargs)));
+ }
+ if (pre)
+ (*pre)(use_layer ? null_mount : mp);
+ remount_ro(tc, mp, expected_errno);
+ if (post)
+ (*post)(use_layer ? null_mount : mp);
+ if (use_layer)
+ RL(rump_sys_unmount(null_mount, 0));
+}
+
+static void
+noneopen(const atf_tc_t *tc, const char *mp)
+{
+
+ basic_test(tc, mp, 0, false, NULL, NULL);
+}
+
+static void
+readopen(const atf_tc_t *tc, const char *mp)
+{
+
+ basic_test(tc, mp, 0, false, open_file_ro, close_file);
+}
+
+static void
+writeopen(const atf_tc_t *tc, const char *mp)
+{
+
+ basic_test(tc, mp, EBUSY, false, open_file_rw, close_file);
+}
+
+static void
+read_unlinked(const atf_tc_t *tc, const char *mp)
+{
+
+ basic_test(tc, mp, EBUSY, false, open_file_ro_unlink, close_file);
+}
+
+static void
+layer_noneopen(const atf_tc_t *tc, const char *mp)
+{
+
+ basic_test(tc, mp, 0, true, NULL, NULL);
+}
+
+static void
+layer_readopen(const atf_tc_t *tc, const char *mp)
+{
+
+ basic_test(tc, mp, 0, true, open_file_ro, close_file);
+}
+
+static void
+layer_writeopen(const atf_tc_t *tc, const char *mp)
+{
+
+ basic_test(tc, mp, EBUSY, true, open_file_rw, close_file);
+}
+
+static void
+layer_read_unlinked(const atf_tc_t *tc, const char *mp)
+{
+
+ basic_test(tc, mp, EBUSY, true, open_file_ro_unlink, close_file);
+}
+
+ATF_TC_FSAPPLY(noneopen, "remount r/o with no file open");
+ATF_TC_FSAPPLY(readopen, "remount r/o with file open for reading");
+ATF_TC_FSAPPLY(writeopen, "remount r/o with file open for writing");
+ATF_TC_FSAPPLY(read_unlinked,
+ "remount r/o with unlinked file open for reading");
+ATF_TC_FSAPPLY(layer_noneopen, "remount r/o with no file open on layer");
+ATF_TC_FSAPPLY(layer_readopen,
+ "remount r/o with file open for reading on layer");
+ATF_TC_FSAPPLY(layer_writeopen,
+ "remount r/o with file open for writing on layer");
+ATF_TC_FSAPPLY(layer_read_unlinked,
+ "remount r/o with unlinked file open for reading on layer");
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_FSAPPLY(noneopen);
+ ATF_TP_FSAPPLY(readopen);
+ ATF_TP_FSAPPLY(writeopen);
+ ATF_TP_FSAPPLY(read_unlinked);
+ ATF_TP_FSAPPLY(layer_noneopen);
+ ATF_TP_FSAPPLY(layer_readopen);
+ ATF_TP_FSAPPLY(layer_writeopen);
+ ATF_TP_FSAPPLY(layer_read_unlinked);
+
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/vfs/t_union.c b/contrib/netbsd-tests/fs/vfs/t_union.c
new file mode 100644
index 000000000000..5da1ea1ff39b
--- /dev/null
+++ b/contrib/netbsd-tests/fs/vfs/t_union.c
@@ -0,0 +1,204 @@
+/* $NetBSD: t_union.c,v 1.9 2017/01/13 21:30:40 christos Exp $ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <atf-c.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include <miscfs/union/union.h>
+
+#include "h_macros.h"
+#include "../common/h_fsmacros.h"
+
+#define MSTR "magic bus"
+
+static void
+xput_tfile(const char *mp, const char *path)
+{
+ char pathb[MAXPATHLEN];
+ int fd;
+
+ strcpy(pathb, mp);
+ strcat(pathb, "/");
+ strcat(pathb, path);
+
+ RL(fd = rump_sys_open(pathb, O_CREAT | O_RDWR, 0777));
+ if (rump_sys_write(fd, MSTR, sizeof(MSTR)) != sizeof(MSTR))
+ atf_tc_fail_errno("write to testfile");
+ RL(rump_sys_close(fd));
+}
+
+static int
+xread_tfile(const char *mp, const char *path)
+{
+ char pathb[MAXPATHLEN];
+ char buf[128];
+ int fd;
+
+ strcpy(pathb, mp);
+ strcat(pathb, "/");
+ strcat(pathb, path);
+
+ fd = rump_sys_open(pathb, O_RDONLY);
+ if (fd == -1)
+ return errno;
+ if (rump_sys_read(fd, buf, sizeof(buf)) == -1)
+ atf_tc_fail_errno("read tfile");
+ RL(rump_sys_close(fd));
+ if (strcmp(buf, MSTR) == 0)
+ return 0;
+ return EPROGMISMATCH;
+}
+
+/*
+ * Mount a unionfs for testing. Before calling, "mp" contains
+ * the upper layer. Lowerpath is constructed so that the directory
+ * contains rumpfs.
+ */
+static void
+mountunion(const char *mp, char *lowerpath)
+{
+ struct union_args unionargs;
+
+ sprintf(lowerpath, "/lower");
+ rump_sys_mkdir(lowerpath, 0777);
+
+ /* mount the union with our testfs as the upper layer */
+ memset(&unionargs, 0, sizeof(unionargs));
+ unionargs.target = lowerpath;
+ unionargs.mntflags = UNMNT_BELOW;
+
+ if (rump_sys_mount(MOUNT_UNION, mp, 0,
+ &unionargs, sizeof(unionargs)) == -1) {
+ if (errno == EOPNOTSUPP) {
+ atf_tc_skip("fs does not support VOP_WHITEOUT");
+ } else {
+ atf_tc_fail_errno("union mount");
+ }
+ }
+}
+
+#if 0
+static void
+toggleroot(void)
+{
+ static int status;
+
+ status ^= MNT_RDONLY;
+
+ printf("0x%x\n", status);
+ RL(rump_sys_mount(MOUNT_RUMPFS, "/", status | MNT_UPDATE, NULL, 0));
+}
+#endif
+
+#define TFILE "tensti"
+#define TDIR "testdir"
+#define TDFILE TDIR "/indir"
+
+static void
+basic(const atf_tc_t *tc, const char *mp)
+{
+ char lowerpath[MAXPATHLEN];
+ char dbuf[8192];
+ struct stat sb;
+ struct dirent *dp;
+ int error, fd, dsize;
+
+ mountunion(mp, lowerpath);
+
+ /* create a file in the lower layer */
+ xput_tfile(lowerpath, TFILE);
+
+ /* first, test we can read the old file from the new namespace */
+ error = xread_tfile(mp, TFILE);
+ if (error != 0)
+ atf_tc_fail("union compare failed: %d (%s)",
+ error, strerror(error));
+
+ /* then, test upper layer writes don't affect the lower layer */
+ xput_tfile(mp, "kiekko");
+ if ((error = xread_tfile(lowerpath, "kiekko")) != ENOENT)
+ atf_tc_fail("invisibility failed: %d (%s)",
+ error, strerror(error));
+
+ /* check that we can whiteout stuff in the upper layer */
+ FSTEST_ENTER();
+ RL(rump_sys_unlink(TFILE));
+ ATF_REQUIRE_ERRNO(ENOENT, rump_sys_stat(TFILE, &sb) == -1);
+ FSTEST_EXIT();
+
+ /* check that the removed node is not in the directory listing */
+ RL(fd = rump_sys_open(mp, O_RDONLY));
+ RL(dsize = rump_sys_getdents(fd, dbuf, sizeof(dbuf)));
+ for (dp = (struct dirent *)dbuf;
+ (char *)dp < dbuf + dsize;
+ dp = _DIRENT_NEXT(dp)) {
+ if (strcmp(dp->d_name, TFILE) == 0 && dp->d_type != DT_WHT)
+ atf_tc_fail("removed file non-white-outed");
+ }
+ RL(rump_sys_close(fd));
+
+ RL(rump_sys_unmount(mp, 0));
+}
+
+static void
+whiteout(const atf_tc_t *tc, const char *mp)
+{
+ char lower[MAXPATHLEN];
+ struct stat sb;
+ void *fsarg;
+
+ /*
+ * XXX: use ffs here to make sure any screwups in rumpfs don't
+ * affect the test
+ */
+ RL(ffs_fstest_newfs(tc, &fsarg, "daimage", 1024*1024*5, NULL));
+ RL(ffs_fstest_mount(tc, fsarg, "/lower", 0));
+
+ /* create a file in the lower layer */
+ RL(rump_sys_chdir("/lower"));
+ RL(rump_sys_mkdir(TDIR, 0777));
+ RL(rump_sys_mkdir(TDFILE, 0777));
+ RL(rump_sys_chdir("/"));
+
+ RL(ffs_fstest_unmount(tc, "/lower", 0));
+ RL(ffs_fstest_mount(tc, fsarg, "/lower", MNT_RDONLY));
+
+ mountunion(mp, lower);
+
+ FSTEST_ENTER();
+ ATF_REQUIRE_ERRNO(ENOTEMPTY, rump_sys_rmdir(TDIR) == -1);
+ RL(rump_sys_rmdir(TDFILE));
+ RL(rump_sys_rmdir(TDIR));
+ ATF_REQUIRE_ERRNO(ENOENT, rump_sys_stat(TDFILE, &sb) == -1);
+ ATF_REQUIRE_ERRNO(ENOENT, rump_sys_stat(TDIR, &sb) == -1);
+
+ RL(rump_sys_mkdir(TDIR, 0777));
+ RL(rump_sys_stat(TDIR, &sb));
+ ATF_REQUIRE_ERRNO(ENOENT, rump_sys_stat(TDFILE, &sb) == -1);
+ FSTEST_EXIT();
+
+ RL(rump_sys_unmount(mp, 0));
+}
+
+ATF_TC_FSAPPLY(basic, "check basic union functionality");
+ATF_TC_FSAPPLY(whiteout, "create whiteout in upper layer");
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_FSAPPLY(basic);
+ ATF_TP_FSAPPLY(whiteout);
+
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/vfs/t_unpriv.c b/contrib/netbsd-tests/fs/vfs/t_unpriv.c
new file mode 100644
index 000000000000..6536c21d9572
--- /dev/null
+++ b/contrib/netbsd-tests/fs/vfs/t_unpriv.c
@@ -0,0 +1,239 @@
+/* $NetBSD: t_unpriv.c,v 1.13 2017/01/13 21:30:40 christos Exp $ */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <atf-c.h>
+#include <libgen.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include <rump/rump_syscalls.h>
+#include <rump/rump.h>
+
+#include "../common/h_fsmacros.h"
+#include "h_macros.h"
+
+#define USES_OWNER \
+ if (FSTYPE_MSDOS(tc)) \
+ atf_tc_skip("owner not supported by file system")
+
+static void
+owner(const atf_tc_t *tc, const char *mp)
+{
+
+ USES_OWNER;
+
+ FSTEST_ENTER();
+
+ rump_pub_lwproc_rfork(RUMP_RFCFDG);
+ if (rump_sys_setuid(1) == -1)
+ atf_tc_fail_errno("setuid");
+ if (rump_sys_chown(".", 1, -1) != -1 || errno != EPERM)
+ atf_tc_fail_errno("chown");
+ if (rump_sys_chmod(".", 0000) != -1 || errno != EPERM)
+ atf_tc_fail_errno("chmod");
+ rump_pub_lwproc_releaselwp();
+
+ if (rump_sys_chown(".", 1, -1) == -1)
+ atf_tc_fail_errno("chown");
+
+ rump_pub_lwproc_rfork(RUMP_RFCFDG);
+ if (rump_sys_setuid(1) == -1)
+ atf_tc_fail_errno("setuid");
+ if (rump_sys_chown(".", 1, -1) == -1)
+ atf_tc_fail_errno("chown");
+ if (rump_sys_chmod(".", 0000) == -1)
+ atf_tc_fail_errno("chmod");
+ rump_pub_lwproc_releaselwp();
+
+ FSTEST_EXIT();
+}
+
+static void
+dirperms(const atf_tc_t *tc, const char *mp)
+{
+ char name[] = "dir.test/file.test";
+ char *dir = dirname(name);
+ int fd;
+
+ if (FSTYPE_SYSVBFS(tc))
+ atf_tc_skip("directories not supported by file system");
+
+ FSTEST_ENTER();
+
+ if (rump_sys_mkdir(dir, 0777) == -1)
+ atf_tc_fail_errno("mkdir");
+
+ rump_pub_lwproc_rfork(RUMP_RFCFDG);
+ if (rump_sys_setuid(1) == -1)
+ atf_tc_fail_errno("setuid");
+ if (FSTYPE_ZFS(tc))
+ atf_tc_expect_fail("PR kern/47656: Test known to be broken");
+ if (rump_sys_open(name, O_RDWR|O_CREAT, 0666) != -1 || errno != EACCES)
+ atf_tc_fail_errno("open");
+ rump_pub_lwproc_releaselwp();
+
+ if ((fd = rump_sys_open(name, O_RDWR|O_CREAT, 0666)) == -1)
+ atf_tc_fail_errno("open");
+ if (rump_sys_close(fd) == -1)
+ atf_tc_fail_errno("close");
+
+ rump_pub_lwproc_rfork(RUMP_RFCFDG);
+ if (rump_sys_setuid(1) == -1)
+ atf_tc_fail_errno("setuid");
+ if (rump_sys_unlink(name) != -1 || errno != EACCES)
+ atf_tc_fail_errno("unlink");
+ rump_pub_lwproc_releaselwp();
+
+ if (rump_sys_unlink(name) == -1)
+ atf_tc_fail_errno("unlink");
+
+ if (rump_sys_rmdir(dir) == -1)
+ atf_tc_fail_errno("rmdir");
+
+ FSTEST_EXIT();
+}
+
+static void
+times(const atf_tc_t *tc, const char *mp)
+{
+ const char *name = "file.test";
+ int fd;
+ unsigned int i, j;
+ struct timeval tmv[2];
+ static struct timeval tmvs[] = {
+ { QUAD_MIN, 0 },
+ { 0, 0 },
+ { QUAD_MAX, 999999 }
+ };
+
+ FSTEST_ENTER();
+
+ if ((fd = rump_sys_open(name, O_RDWR|O_CREAT, 0666)) == -1)
+ atf_tc_fail_errno("open");
+ if (rump_sys_close(fd) == -1)
+ atf_tc_fail_errno("close");
+
+ rump_pub_lwproc_rfork(RUMP_RFCFDG);
+ if (rump_sys_setuid(1) == -1)
+ atf_tc_fail_errno("setuid");
+ if (FSTYPE_ZFS(tc))
+ atf_tc_expect_fail("PR kern/47656: Test known to be broken");
+ if (rump_sys_utimes(name, NULL) != -1 || errno != EACCES)
+ atf_tc_fail_errno("utimes");
+ rump_pub_lwproc_releaselwp();
+
+ if (rump_sys_utimes(name, NULL) == -1)
+ atf_tc_fail_errno("utimes");
+
+ for (i = 0; i < sizeof(tmvs) / sizeof(tmvs[0]); i++) {
+ for (j = 0; j < sizeof(tmvs) / sizeof(tmvs[0]); j++) {
+ tmv[0] = tmvs[i];
+ tmv[1] = tmvs[j];
+ rump_pub_lwproc_rfork(RUMP_RFCFDG);
+ if (rump_sys_setuid(1) == -1)
+ atf_tc_fail_errno("setuid");
+ if (rump_sys_utimes(name, tmv) != -1 || errno != EPERM)
+ atf_tc_fail_errno("utimes");
+ rump_pub_lwproc_releaselwp();
+
+ if (rump_sys_utimes(name, tmv) == -1)
+ atf_tc_fail_errno("utimes");
+ }
+ }
+
+ if (rump_sys_unlink(name) == -1)
+ atf_tc_fail_errno("unlink");
+
+ FSTEST_EXIT();
+}
+
+static void
+flags(const atf_tc_t *tc, const char *mp)
+{
+ const char *name = "file.test";
+ int fd, fflags;
+ struct stat st;
+
+ FSTEST_ENTER();
+
+ if ((fd = rump_sys_open(name, O_RDWR|O_CREAT, 0666)) == -1)
+ atf_tc_fail_errno("open");
+ if (rump_sys_close(fd) == -1)
+ atf_tc_fail_errno("close");
+
+ if (rump_sys_stat(name, &st) == -1)
+ atf_tc_fail_errno("stat");
+ if (FSTYPE_ZFS(tc))
+ atf_tc_expect_fail("PR kern/47656: Test known to be broken");
+ if (rump_sys_chflags(name, st.st_flags) == -1) {
+ if (errno == EOPNOTSUPP)
+ atf_tc_skip("file flags not supported by file system");
+ atf_tc_fail_errno("chflags");
+ }
+
+ fflags = st.st_flags | UF_IMMUTABLE;
+
+ rump_pub_lwproc_rfork(RUMP_RFCFDG);
+ if (rump_sys_setuid(1) == -1)
+ atf_tc_fail_errno("setuid");
+ fflags |= UF_IMMUTABLE;
+ if (rump_sys_chflags(name, fflags) != -1 || errno != EPERM)
+ atf_tc_fail_errno("chflags");
+ rump_pub_lwproc_releaselwp();
+
+ if (rump_sys_chflags(name, fflags) == -1)
+ atf_tc_fail_errno("chflags");
+
+ fflags &= ~UF_IMMUTABLE;
+ if (rump_sys_chflags(name, fflags) == -1)
+ atf_tc_fail_errno("chflags");
+
+ if (rump_sys_unlink(name) == -1)
+ atf_tc_fail_errno("unlink");
+
+ FSTEST_EXIT();
+}
+
+ATF_TC_FSAPPLY(owner, "owner unprivileged checks");
+ATF_TC_FSAPPLY(dirperms, "directory permission checks");
+ATF_TC_FSAPPLY(times, "time set checks");
+ATF_TC_FSAPPLY(flags, "file flags checks");
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_FSAPPLY(owner);
+ ATF_TP_FSAPPLY(dirperms);
+ ATF_TP_FSAPPLY(times);
+ ATF_TP_FSAPPLY(flags);
+
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/vfs/t_vfsops.c b/contrib/netbsd-tests/fs/vfs/t_vfsops.c
new file mode 100644
index 000000000000..bbed9f9d3089
--- /dev/null
+++ b/contrib/netbsd-tests/fs/vfs/t_vfsops.c
@@ -0,0 +1,211 @@
+/* $NetBSD: t_vfsops.c,v 1.12 2017/01/13 21:30:40 christos Exp $ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+
+#include <atf-c.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <rump/rump_syscalls.h>
+#include <rump/rump.h>
+
+#include "../common/h_fsmacros.h"
+#include "h_macros.h"
+
+static void
+tmount(const atf_tc_t *tc, const char *path)
+{
+
+ return;
+}
+
+static void
+tstatvfs(const atf_tc_t *tc, const char *path)
+{
+ const char *fstype = atf_tc_get_md_var(tc, "X-fs.mntname");
+ struct statvfs svb;
+
+ if (rump_sys_statvfs1(path, &svb, ST_WAIT) == -1)
+ atf_tc_fail_errno("statvfs");
+
+ ATF_REQUIRE(svb.f_namemax > 0 && svb.f_namemax <= MAXNAMLEN);
+ if (!(FSTYPE_PUFFS(tc) || FSTYPE_P2K_FFS(tc)))
+ ATF_REQUIRE_STREQ(svb.f_fstypename, fstype);
+ ATF_REQUIRE_STREQ(svb.f_mntonname, path);
+}
+
+static void
+tsync(const atf_tc_t *tc, const char *path)
+{
+
+ rump_sys_sync();
+}
+
+#define MAGICSTR "just a string, I like A"
+static void
+tfilehandle(const atf_tc_t *tc, const char *path)
+{
+ char fpath[MAXPATHLEN];
+ char buf[sizeof(MAGICSTR)];
+ size_t fhsize;
+ void *fhp;
+ int fd;
+
+ sprintf(fpath, "%s/file", path);
+ fd = rump_sys_open(fpath, O_RDWR | O_CREAT, 0777);
+ if (fd == -1)
+ atf_tc_fail_errno("open");
+
+ if (rump_sys_write(fd, MAGICSTR, sizeof(MAGICSTR)) != sizeof(MAGICSTR))
+ atf_tc_fail("write to file");
+ rump_sys_close(fd);
+
+ /*
+ * Get file handle size.
+ * This also weeds out unsupported file systems.
+ */
+ fhsize = 0;
+ if (rump_sys_getfh(fpath, NULL, &fhsize) == -1) {
+ if (errno == EOPNOTSUPP) {
+ atf_tc_skip("file handles not supported");
+ } else if (errno != E2BIG) {
+ atf_tc_fail_errno("getfh size");
+ }
+ }
+
+ fhp = malloc(fhsize);
+ if (rump_sys_getfh(fpath, fhp, &fhsize) == -1)
+ atf_tc_fail_errno("getfh");
+
+ /* open file based on file handle */
+ fd = rump_sys_fhopen(fhp, fhsize, O_RDONLY);
+ if (fd == -1) {
+ atf_tc_fail_errno("fhopen");
+ }
+
+ /* check that we got the same file */
+ if (rump_sys_read(fd, buf, sizeof(buf)) != sizeof(MAGICSTR))
+ atf_tc_fail("read fhopened file");
+
+ ATF_REQUIRE_STREQ(buf, MAGICSTR);
+
+ rump_sys_close(fd);
+}
+
+#define FNAME "a_file"
+static void
+tfhremove(const atf_tc_t *tc, const char *path)
+{
+ size_t fhsize;
+ void *fhp;
+ int fd;
+
+ RL(rump_sys_chdir(path));
+ RL(fd = rump_sys_open(FNAME, O_RDWR | O_CREAT, 0777));
+ RL(rump_sys_close(fd));
+
+ fhsize = 0;
+ if (rump_sys_getfh(FNAME, NULL, &fhsize) == -1) {
+ if (errno == EOPNOTSUPP) {
+ atf_tc_skip("file handles not supported");
+ } else if (errno != E2BIG) {
+ atf_tc_fail_errno("getfh size");
+ }
+ }
+
+ fhp = malloc(fhsize);
+ RL(rump_sys_getfh(FNAME, fhp, &fhsize));
+ RL(rump_sys_unlink(FNAME));
+
+ if (FSTYPE_LFS(tc))
+ atf_tc_expect_fail("fhopen() for removed file succeeds "
+ "(PR kern/43745)");
+ ATF_REQUIRE_ERRNO(ESTALE, rump_sys_fhopen(fhp, fhsize, O_RDONLY) == -1);
+ atf_tc_expect_pass();
+
+ RL(rump_sys_chdir("/"));
+}
+#undef FNAME
+
+/*
+ * This test only checks the file system doesn't crash. We *might*
+ * try a valid file handle.
+ */
+static void
+tfhinval(const atf_tc_t *tc, const char *path)
+{
+ size_t fhsize;
+ void *fhp;
+ unsigned long seed;
+ int fd;
+
+ srandom(seed = time(NULL));
+ printf("RNG seed %lu\n", seed);
+
+ RL(rump_sys_chdir(path));
+ fhsize = 0;
+ if (rump_sys_getfh(".", NULL, &fhsize) == -1) {
+ if (errno == EOPNOTSUPP) {
+ atf_tc_skip("file handles not supported");
+ } else if (errno != E2BIG) {
+ atf_tc_fail_errno("getfh size");
+ }
+ }
+
+ fhp = malloc(fhsize);
+ tests_makegarbage(fhp, fhsize);
+ fd = rump_sys_fhopen(fhp, fhsize, O_RDWR);
+ if (fd != -1)
+ rump_sys_close(fd);
+
+ RL(rump_sys_chdir("/"));
+}
+
+ATF_TC_FSAPPLY(tmount, "mount/unmount");
+ATF_TC_FSAPPLY(tstatvfs, "statvfs");
+ATF_TC_FSAPPLY(tsync, "sync");
+ATF_TC_FSAPPLY(tfilehandle, "file handles");
+ATF_TC_FSAPPLY(tfhremove, "fhtovp for removed file");
+ATF_TC_FSAPPLY(tfhinval, "fhopen invalid filehandle");
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_FSAPPLY(tmount);
+ ATF_TP_FSAPPLY(tstatvfs);
+ ATF_TP_FSAPPLY(tsync);
+ ATF_TP_FSAPPLY(tfilehandle);
+ ATF_TP_FSAPPLY(tfhremove);
+ ATF_TP_FSAPPLY(tfhinval);
+
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/vfs/t_vnops.c b/contrib/netbsd-tests/fs/vfs/t_vnops.c
new file mode 100644
index 000000000000..7da53c8d4386
--- /dev/null
+++ b/contrib/netbsd-tests/fs/vfs/t_vnops.c
@@ -0,0 +1,1080 @@
+/* $NetBSD: t_vnops.c,v 1.59 2017/01/13 21:30:40 christos Exp $ */
+
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <atf-c.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rump/rump_syscalls.h>
+#include <rump/rump.h>
+
+#include "../common/h_fsmacros.h"
+#include "h_macros.h"
+
+#define TESTFILE "afile"
+
+#define USES_DIRS \
+ if (FSTYPE_SYSVBFS(tc)) \
+ atf_tc_skip("directories not supported by file system")
+
+#define USES_SYMLINKS \
+ if (FSTYPE_SYSVBFS(tc) || FSTYPE_MSDOS(tc)) \
+ atf_tc_skip("symlinks not supported by file system")
+
+static char *
+md(char *buf, size_t buflen, const char *base, const char *tail)
+{
+
+ snprintf(buf, buflen, "%s/%s", base, tail);
+ return buf;
+}
+
+static void
+lookup_simple(const atf_tc_t *tc, const char *mountpath)
+{
+ char pb[MAXPATHLEN], final[MAXPATHLEN];
+ struct stat sb1, sb2;
+
+ strcpy(final, mountpath);
+ snprintf(pb, sizeof(pb), "%s/../%s", mountpath, basename(final));
+ if (rump_sys_stat(pb, &sb1) == -1)
+ atf_tc_fail_errno("stat 1");
+
+ snprintf(pb, sizeof(pb), "%s/./../%s", mountpath, basename(final));
+ if (rump_sys_stat(pb, &sb2) == -1)
+ atf_tc_fail_errno("stat 2");
+
+ ATF_REQUIRE(memcmp(&sb1, &sb2, sizeof(sb1)) == 0);
+}
+
+static void
+lookup_complex(const atf_tc_t *tc, const char *mountpath)
+{
+ char pb[MAXPATHLEN];
+ struct stat sb1, sb2;
+ struct timespec atplus1, onesec;
+
+ USES_DIRS;
+
+ snprintf(pb, sizeof(pb), "%s/dir", mountpath);
+ if (rump_sys_mkdir(pb, 0777) == -1)
+ atf_tc_fail_errno("mkdir");
+ if (rump_sys_stat(pb, &sb1) == -1)
+ atf_tc_fail_errno("stat 1");
+
+ snprintf(pb, sizeof(pb), "%s/./dir/../././dir/.", mountpath);
+ if (rump_sys_stat(pb, &sb2) == -1)
+ atf_tc_fail_errno("stat 2");
+
+ /*
+ * The lookup is permitted to modify the access time of
+ * any directories searched - such a directory is the
+ * subject of this test. Any difference should cause
+ * the 2nd lookup atime tp be >= the first, if it is ==, all is
+ * OK (atime is not required to be modified by the search, or
+ * both references may happen within the came clock tick), if the
+ * 2nd lookup atime is > the first, but not "too much" greater,
+ * just set it back, so the memcmp just below succeeds
+ * (assuming all else is OK).
+ */
+ onesec.tv_sec = 1;
+ onesec.tv_nsec = 0;
+ timespecadd(&sb1.st_atimespec, &onesec, &atplus1);
+ if (timespeccmp(&sb2.st_atimespec, &sb1.st_atimespec, >) &&
+ timespeccmp(&sb2.st_atimespec, &atplus1, <))
+ sb2.st_atimespec = sb1.st_atimespec;
+
+ if (memcmp(&sb1, &sb2, sizeof(sb1)) != 0) {
+ printf("what\tsb1\t\tsb2\n");
+
+#define FIELD(FN) \
+ printf(#FN "\t%lld\t%lld\n", \
+ (long long)sb1.FN, (long long)sb2.FN)
+#define TIME(FN) \
+ printf(#FN "\t%lld.%ld\t%lld.%ld\n", \
+ (long long)sb1.FN.tv_sec, sb1.FN.tv_nsec, \
+ (long long)sb2.FN.tv_sec, sb2.FN.tv_nsec)
+
+ FIELD(st_dev);
+ FIELD(st_mode);
+ FIELD(st_ino);
+ FIELD(st_nlink);
+ FIELD(st_uid);
+ FIELD(st_gid);
+ FIELD(st_rdev);
+ TIME(st_atimespec);
+ TIME(st_mtimespec);
+ TIME(st_ctimespec);
+ TIME(st_birthtimespec);
+ FIELD(st_size);
+ FIELD(st_blocks);
+ FIELD(st_flags);
+ FIELD(st_gen);
+
+#undef FIELD
+#undef TIME
+
+ atf_tc_fail("stat results differ, see ouput for more details");
+ }
+}
+
+static void
+dir_simple(const atf_tc_t *tc, const char *mountpath)
+{
+ char pb[MAXPATHLEN];
+ struct stat sb;
+
+ USES_DIRS;
+
+ /* check we can create directories */
+ snprintf(pb, sizeof(pb), "%s/dir", mountpath);
+ if (rump_sys_mkdir(pb, 0777) == -1)
+ atf_tc_fail_errno("mkdir");
+ if (rump_sys_stat(pb, &sb) == -1)
+ atf_tc_fail_errno("stat new directory");
+
+ /* check we can remove then and that it makes them unreachable */
+ if (rump_sys_rmdir(pb) == -1)
+ atf_tc_fail_errno("rmdir");
+ if (rump_sys_stat(pb, &sb) != -1 || errno != ENOENT)
+ atf_tc_fail("ENOENT expected from stat");
+}
+
+static void
+dir_notempty(const atf_tc_t *tc, const char *mountpath)
+{
+ char pb[MAXPATHLEN], pb2[MAXPATHLEN];
+ int fd, rv;
+
+ USES_DIRS;
+
+ /* check we can create directories */
+ snprintf(pb, sizeof(pb), "%s/dir", mountpath);
+ if (rump_sys_mkdir(pb, 0777) == -1)
+ atf_tc_fail_errno("mkdir");
+
+ snprintf(pb2, sizeof(pb2), "%s/dir/file", mountpath);
+ fd = rump_sys_open(pb2, O_RDWR | O_CREAT, 0777);
+ if (fd == -1)
+ atf_tc_fail_errno("create file");
+ rump_sys_close(fd);
+
+ rv = rump_sys_rmdir(pb);
+ if (rv != -1 || errno != ENOTEMPTY)
+ atf_tc_fail("non-empty directory removed succesfully");
+
+ if (rump_sys_unlink(pb2) == -1)
+ atf_tc_fail_errno("cannot remove dir/file");
+
+ if (rump_sys_rmdir(pb) == -1)
+ atf_tc_fail_errno("remove directory");
+}
+
+static void
+dir_rmdirdotdot(const atf_tc_t *tc, const char *mp)
+{
+ char pb[MAXPATHLEN];
+ int xerrno;
+
+ USES_DIRS;
+
+ FSTEST_ENTER();
+ RL(rump_sys_mkdir("test", 0777));
+ RL(rump_sys_chdir("test"));
+
+ RL(rump_sys_mkdir("subtest", 0777));
+ RL(rump_sys_chdir("subtest"));
+
+ md(pb, sizeof(pb), mp, "test/subtest");
+ RL(rump_sys_rmdir(pb));
+ md(pb, sizeof(pb), mp, "test");
+ RL(rump_sys_rmdir(pb));
+
+ if (FSTYPE_NFS(tc))
+ xerrno = ESTALE;
+ else
+ xerrno = ENOENT;
+ ATF_REQUIRE_ERRNO(xerrno, rump_sys_chdir("..") == -1);
+ FSTEST_EXIT();
+}
+
+static void
+checkfile(const char *path, struct stat *refp)
+{
+ char buf[MAXPATHLEN];
+ struct stat sb;
+ static int n = 1;
+
+ md(buf, sizeof(buf), path, "file");
+ if (rump_sys_stat(buf, &sb) == -1)
+ atf_tc_fail_errno("cannot stat file %d (%s)", n, buf);
+ if (memcmp(&sb, refp, sizeof(sb)) != 0)
+ atf_tc_fail("stat mismatch %d", n);
+ n++;
+}
+
+static void
+rename_dir(const atf_tc_t *tc, const char *mp)
+{
+ char pb1[MAXPATHLEN], pb2[MAXPATHLEN], pb3[MAXPATHLEN];
+ struct stat ref, sb;
+
+ if (FSTYPE_RUMPFS(tc))
+ atf_tc_skip("rename not supported by file system");
+
+ USES_DIRS;
+
+ md(pb1, sizeof(pb1), mp, "dir1");
+ if (rump_sys_mkdir(pb1, 0777) == -1)
+ atf_tc_fail_errno("mkdir 1");
+
+ md(pb2, sizeof(pb2), mp, "dir2");
+ if (rump_sys_mkdir(pb2, 0777) == -1)
+ atf_tc_fail_errno("mkdir 2");
+ md(pb2, sizeof(pb2), mp, "dir2/subdir");
+ if (rump_sys_mkdir(pb2, 0777) == -1)
+ atf_tc_fail_errno("mkdir 3");
+
+ md(pb3, sizeof(pb3), mp, "dir1/file");
+ if (rump_sys_mknod(pb3, S_IFREG | 0777, -1) == -1)
+ atf_tc_fail_errno("create file");
+ if (rump_sys_stat(pb3, &ref) == -1)
+ atf_tc_fail_errno("stat of file");
+
+ /*
+ * First try ops which should succeed.
+ */
+
+ /* rename within directory */
+ md(pb3, sizeof(pb3), mp, "dir3");
+ if (rump_sys_rename(pb1, pb3) == -1)
+ atf_tc_fail_errno("rename 1");
+ checkfile(pb3, &ref);
+
+ /* rename directory onto itself (two ways, should fail) */
+ md(pb1, sizeof(pb1), mp, "dir3/.");
+ if (rump_sys_rename(pb1, pb3) != -1 || errno != EINVAL)
+ atf_tc_fail_errno("rename 2");
+ if (rump_sys_rename(pb3, pb1) != -1 || errno != EISDIR)
+ atf_tc_fail_errno("rename 3");
+
+ checkfile(pb3, &ref);
+
+ /* rename father of directory into directory */
+ md(pb1, sizeof(pb1), mp, "dir2/dir");
+ md(pb2, sizeof(pb2), mp, "dir2");
+ if (rump_sys_rename(pb2, pb1) != -1 || errno != EINVAL)
+ atf_tc_fail_errno("rename 4");
+
+ /* same for grandfather */
+ md(pb1, sizeof(pb1), mp, "dir2/subdir/dir2");
+ if (rump_sys_rename(pb2, pb1) != -1 || errno != EINVAL)
+ atf_tc_fail("rename 5");
+
+ checkfile(pb3, &ref);
+
+ /* rename directory over a non-empty directory */
+ if (rump_sys_rename(pb2, pb3) != -1 || errno != ENOTEMPTY)
+ atf_tc_fail("rename 6");
+
+ /* cross-directory rename */
+ md(pb1, sizeof(pb1), mp, "dir3");
+ md(pb2, sizeof(pb2), mp, "dir2/somedir");
+ if (rump_sys_rename(pb1, pb2) == -1)
+ atf_tc_fail_errno("rename 7");
+ checkfile(pb2, &ref);
+
+ /* move to parent directory */
+ md(pb1, sizeof(pb1), mp, "dir2/somedir/../../dir3");
+ if (rump_sys_rename(pb2, pb1) == -1)
+ atf_tc_fail_errno("rename 8");
+ md(pb1, sizeof(pb1), mp, "dir2/../dir3");
+ checkfile(pb1, &ref);
+
+ /* atomic cross-directory rename */
+ md(pb3, sizeof(pb3), mp, "dir2/subdir");
+ if (rump_sys_rename(pb1, pb3) == -1)
+ atf_tc_fail_errno("rename 9");
+ checkfile(pb3, &ref);
+
+ /* rename directory over an empty directory */
+ md(pb1, sizeof(pb1), mp, "parent");
+ md(pb2, sizeof(pb2), mp, "parent/dir1");
+ md(pb3, sizeof(pb3), mp, "parent/dir2");
+ RL(rump_sys_mkdir(pb1, 0777));
+ RL(rump_sys_mkdir(pb2, 0777));
+ RL(rump_sys_mkdir(pb3, 0777));
+ RL(rump_sys_rename(pb2, pb3));
+
+ RL(rump_sys_stat(pb1, &sb));
+ if (! FSTYPE_MSDOS(tc))
+ ATF_CHECK_EQ(sb.st_nlink, 3);
+ RL(rump_sys_rmdir(pb3));
+ RL(rump_sys_rmdir(pb1));
+}
+
+static void
+rename_dotdot(const atf_tc_t *tc, const char *mp)
+{
+
+ if (FSTYPE_RUMPFS(tc))
+ atf_tc_skip("rename not supported by file system");
+
+ USES_DIRS;
+
+ if (rump_sys_chdir(mp) == -1)
+ atf_tc_fail_errno("chdir mountpoint");
+
+ if (rump_sys_mkdir("dir1", 0777) == -1)
+ atf_tc_fail_errno("mkdir 1");
+ if (rump_sys_mkdir("dir2", 0777) == -1)
+ atf_tc_fail_errno("mkdir 2");
+
+ if (rump_sys_rename("dir1", "dir1/..") != -1 || errno != EINVAL)
+ atf_tc_fail_errno("self-dotdot to");
+
+ if (rump_sys_rename("dir1/..", "sometarget") != -1 || errno != EINVAL)
+ atf_tc_fail_errno("self-dotdot from");
+
+ if (rump_sys_rename("dir1", "dir2/..") != -1 || errno != EINVAL)
+ atf_tc_fail("other-dotdot");
+
+ rump_sys_chdir("/");
+}
+
+static void
+rename_reg_nodir(const atf_tc_t *tc, const char *mp)
+{
+ bool haslinks;
+ struct stat sb;
+ ino_t f1ino;
+
+ if (FSTYPE_RUMPFS(tc))
+ atf_tc_skip("rename not supported by file system");
+
+ if (rump_sys_chdir(mp) == -1)
+ atf_tc_fail_errno("chdir mountpoint");
+
+ if (FSTYPE_MSDOS(tc) || FSTYPE_SYSVBFS(tc))
+ haslinks = false;
+ else
+ haslinks = true;
+
+ if (rump_sys_mknod("file1", S_IFREG | 0777, -1) == -1)
+ atf_tc_fail_errno("create file");
+ if (rump_sys_mknod("file2", S_IFREG | 0777, -1) == -1)
+ atf_tc_fail_errno("create file");
+
+ if (rump_sys_stat("file1", &sb) == -1)
+ atf_tc_fail_errno("stat");
+ f1ino = sb.st_ino;
+
+ if (haslinks) {
+ if (rump_sys_link("file1", "file_link") == -1)
+ atf_tc_fail_errno("link");
+ if (rump_sys_stat("file_link", &sb) == -1)
+ atf_tc_fail_errno("stat");
+ ATF_REQUIRE_EQ(sb.st_ino, f1ino);
+ ATF_REQUIRE_EQ(sb.st_nlink, 2);
+ }
+
+ if (rump_sys_stat("file2", &sb) == -1)
+ atf_tc_fail_errno("stat");
+
+ if (rump_sys_rename("file1", "file3") == -1)
+ atf_tc_fail_errno("rename 1");
+ if (rump_sys_stat("file3", &sb) == -1)
+ atf_tc_fail_errno("stat 1");
+ if (haslinks) {
+ ATF_REQUIRE_EQ(sb.st_ino, f1ino);
+ }
+ if (rump_sys_stat("file1", &sb) != -1 || errno != ENOENT)
+ atf_tc_fail_errno("source 1");
+
+ if (rump_sys_rename("file3", "file2") == -1)
+ atf_tc_fail_errno("rename 2");
+ if (rump_sys_stat("file2", &sb) == -1)
+ atf_tc_fail_errno("stat 2");
+ if (haslinks) {
+ ATF_REQUIRE_EQ(sb.st_ino, f1ino);
+ }
+
+ if (rump_sys_stat("file3", &sb) != -1 || errno != ENOENT)
+ atf_tc_fail_errno("source 2");
+
+ if (haslinks) {
+ if (rump_sys_rename("file2", "file_link") == -1)
+ atf_tc_fail_errno("rename hardlink");
+ if (rump_sys_stat("file2", &sb) != -1 || errno != ENOENT)
+ atf_tc_fail_errno("source 3");
+ if (rump_sys_stat("file_link", &sb) == -1)
+ atf_tc_fail_errno("stat 2");
+ ATF_REQUIRE_EQ(sb.st_ino, f1ino);
+ ATF_REQUIRE_EQ(sb.st_nlink, 1);
+ }
+
+ ATF_CHECK_ERRNO(EFAULT, rump_sys_rename("file2", NULL) == -1);
+ ATF_CHECK_ERRNO(EFAULT, rump_sys_rename(NULL, "file2") == -1);
+
+ rump_sys_chdir("/");
+}
+
+/* PR kern/50607 */
+static void
+create_many(const atf_tc_t *tc, const char *mp)
+{
+ char buf[64];
+ int nfiles = 2324; /* #Nancy */
+ int i;
+
+ /* takes forever with many files */
+ if (FSTYPE_MSDOS(tc))
+ nfiles /= 4;
+
+ RL(rump_sys_chdir(mp));
+
+ if (FSTYPE_SYSVBFS(tc)) {
+ /* fs doesn't support many files or subdirectories */
+ nfiles = 5;
+ } else {
+ /* msdosfs doesn't like many entries in the root directory */
+ RL(rump_sys_mkdir("subdir", 0777));
+ RL(rump_sys_chdir("subdir"));
+ }
+
+ /* create them */
+#define TESTFN "testfile"
+ for (i = 0; i < nfiles; i++) {
+ int fd;
+
+ snprintf(buf, sizeof(buf), TESTFN "%d", i);
+ RL(fd = rump_sys_open(buf, O_RDWR|O_CREAT|O_EXCL, 0666));
+ RL(rump_sys_close(fd));
+ }
+
+ /* wipe them out */
+ for (i = 0; i < nfiles; i++) {
+ snprintf(buf, sizeof(buf), TESTFN "%d", i);
+ RLF(rump_sys_unlink(buf), "%s", buf);
+ }
+#undef TESTFN
+
+ rump_sys_chdir("/");
+}
+
+/*
+ * Test creating files with one-character names using all possible
+ * character values. Failures to create the file are ignored as the
+ * characters allowed in file names vary by file system, but at least
+ * we can check that the fs does not crash, and if the file is
+ * successfully created, unlinking it should also succeed.
+ */
+static void
+create_nonalphanum(const atf_tc_t *tc, const char *mp)
+{
+ char buf[64];
+ int i;
+
+ RL(rump_sys_chdir(mp));
+
+ for (i = 0; i < 256; i++) {
+ int fd;
+ snprintf(buf, sizeof(buf), "%c", i);
+ fd = rump_sys_open(buf, O_RDWR|O_CREAT|O_EXCL, 0666);
+ if (fd == -1)
+ continue;
+ RLF(rump_sys_close(fd), "%d", fd);
+ RLF(rump_sys_unlink(buf), "%s", buf);
+ }
+ printf("\n");
+
+ rump_sys_chdir("/");
+}
+
+static void
+create_nametoolong(const atf_tc_t *tc, const char *mp)
+{
+ char *name;
+ int fd;
+ long val;
+ size_t len;
+
+ if (rump_sys_chdir(mp) == -1)
+ atf_tc_fail_errno("chdir mountpoint");
+
+ val = rump_sys_pathconf(".", _PC_NAME_MAX);
+ if (val == -1)
+ atf_tc_fail_errno("pathconf");
+
+ len = val + 1;
+ name = malloc(len+1);
+ if (name == NULL)
+ atf_tc_fail_errno("malloc");
+
+ memset(name, 'a', len);
+ *(name+len) = '\0';
+
+ val = rump_sys_pathconf(".", _PC_NO_TRUNC);
+ if (val == -1)
+ atf_tc_fail_errno("pathconf");
+
+ fd = rump_sys_open(name, O_RDWR|O_CREAT, 0666);
+ if (val != 0 && (fd != -1 || errno != ENAMETOOLONG))
+ atf_tc_fail_errno("open");
+
+ if (val == 0 && rump_sys_close(fd) == -1)
+ atf_tc_fail_errno("close");
+ if (val == 0 && rump_sys_unlink(name) == -1)
+ atf_tc_fail_errno("unlink");
+
+ free(name);
+
+ rump_sys_chdir("/");
+}
+
+static void
+create_exist(const atf_tc_t *tc, const char *mp)
+{
+ const char *name = "hoge";
+ int fd;
+
+ RL(rump_sys_chdir(mp));
+ RL(fd = rump_sys_open(name, O_RDWR|O_CREAT|O_EXCL, 0666));
+ RL(rump_sys_close(fd));
+ RL(rump_sys_unlink(name));
+ RL(fd = rump_sys_open(name, O_RDWR|O_CREAT, 0666));
+ RL(rump_sys_close(fd));
+ RL(fd = rump_sys_open(name, O_RDWR|O_CREAT, 0666));
+ RL(rump_sys_close(fd));
+ ATF_REQUIRE_ERRNO(EEXIST,
+ (fd = rump_sys_open(name, O_RDWR|O_CREAT|O_EXCL, 0666)));
+ RL(rump_sys_unlink(name));
+ RL(rump_sys_chdir("/"));
+}
+
+static void
+rename_nametoolong(const atf_tc_t *tc, const char *mp)
+{
+ char *name;
+ int res, fd;
+ long val;
+ size_t len;
+
+ if (FSTYPE_RUMPFS(tc))
+ atf_tc_skip("rename not supported by file system");
+
+ if (rump_sys_chdir(mp) == -1)
+ atf_tc_fail_errno("chdir mountpoint");
+
+ val = rump_sys_pathconf(".", _PC_NAME_MAX);
+ if (val == -1)
+ atf_tc_fail_errno("pathconf");
+
+ len = val + 1;
+ name = malloc(len+1);
+ if (name == NULL)
+ atf_tc_fail_errno("malloc");
+
+ memset(name, 'a', len);
+ *(name+len) = '\0';
+
+ fd = rump_sys_open("dummy", O_RDWR|O_CREAT, 0666);
+ if (fd == -1)
+ atf_tc_fail_errno("open");
+ if (rump_sys_close(fd) == -1)
+ atf_tc_fail_errno("close");
+
+ val = rump_sys_pathconf(".", _PC_NO_TRUNC);
+ if (val == -1)
+ atf_tc_fail_errno("pathconf");
+
+ res = rump_sys_rename("dummy", name);
+ if (val != 0 && (res != -1 || errno != ENAMETOOLONG))
+ atf_tc_fail_errno("rename");
+
+ if (val == 0 && rump_sys_unlink(name) == -1)
+ atf_tc_fail_errno("unlink");
+
+ free(name);
+
+ rump_sys_chdir("/");
+}
+
+/*
+ * Test creating a symlink whose length is "len" bytes, not including
+ * the terminating NUL.
+ */
+static void
+symlink_len(const atf_tc_t *tc, const char *mp, size_t len)
+{
+ char *buf;
+ int r;
+
+ USES_SYMLINKS;
+
+ RLF(rump_sys_chdir(mp), "%s", mp);
+
+ buf = malloc(len + 1);
+ ATF_REQUIRE(buf);
+ memset(buf, 'a', len);
+ buf[len] = '\0';
+ r = rump_sys_symlink(buf, "afile");
+ if (r == -1) {
+ ATF_REQUIRE_ERRNO(ENAMETOOLONG, r);
+ } else {
+ RL(rump_sys_unlink("afile"));
+ }
+ free(buf);
+
+ RL(rump_sys_chdir("/"));
+}
+
+static void
+symlink_zerolen(const atf_tc_t *tc, const char *mp)
+{
+ symlink_len(tc, mp, 0);
+}
+
+static void
+symlink_long(const atf_tc_t *tc, const char *mp)
+{
+ /*
+ * Test lengths close to powers of two, as those are likely
+ * to be edge cases.
+ */
+ size_t len;
+ int fuzz;
+ for (len = 2; len <= 65536; len *= 2) {
+ for (fuzz = -1; fuzz <= 1; fuzz++) {
+ symlink_len(tc, mp, len + fuzz);
+ }
+ }
+}
+
+static void
+symlink_root(const atf_tc_t *tc, const char *mp)
+{
+
+ USES_SYMLINKS;
+
+ RL(rump_sys_chdir(mp));
+ RL(rump_sys_symlink("/", "foo"));
+ RL(rump_sys_chdir("foo"));
+}
+
+static void
+attrs(const atf_tc_t *tc, const char *mp)
+{
+ struct stat sb, sb2;
+ struct timeval tv[2];
+ int fd;
+
+ FSTEST_ENTER();
+ RL(fd = rump_sys_open(TESTFILE, O_RDWR | O_CREAT, 0755));
+ RL(rump_sys_close(fd));
+ RL(rump_sys_stat(TESTFILE, &sb));
+ if (!(FSTYPE_MSDOS(tc) || FSTYPE_SYSVBFS(tc))) {
+ RL(rump_sys_chown(TESTFILE, 1, 2));
+ sb.st_uid = 1;
+ sb.st_gid = 2;
+ RL(rump_sys_chmod(TESTFILE, 0123));
+ sb.st_mode = (sb.st_mode & ~ACCESSPERMS) | 0123;
+ }
+
+ tv[0].tv_sec = 1000000000; /* need something >1980 for msdosfs */
+ tv[0].tv_usec = 1;
+ tv[1].tv_sec = 1000000002; /* need even seconds for msdosfs */
+ tv[1].tv_usec = 3;
+ RL(rump_sys_utimes(TESTFILE, tv));
+ RL(rump_sys_utimes(TESTFILE, tv)); /* XXX: utimes & birthtime */
+ sb.st_atimespec.tv_sec = 1000000000;
+ sb.st_atimespec.tv_nsec = 1000;
+ sb.st_mtimespec.tv_sec = 1000000002;
+ sb.st_mtimespec.tv_nsec = 3000;
+
+ RL(rump_sys_stat(TESTFILE, &sb2));
+#define CHECK(a) ATF_REQUIRE_EQ(sb.a, sb2.a)
+ if (!(FSTYPE_MSDOS(tc) || FSTYPE_SYSVBFS(tc))) {
+ CHECK(st_uid);
+ CHECK(st_gid);
+ CHECK(st_mode);
+ }
+ if (!FSTYPE_MSDOS(tc)) {
+ /* msdosfs has only access date, not time */
+ CHECK(st_atimespec.tv_sec);
+ }
+ CHECK(st_mtimespec.tv_sec);
+ if (!(FSTYPE_EXT2FS(tc) || FSTYPE_MSDOS(tc) ||
+ FSTYPE_SYSVBFS(tc) || FSTYPE_V7FS(tc))) {
+ CHECK(st_atimespec.tv_nsec);
+ CHECK(st_mtimespec.tv_nsec);
+ }
+#undef CHECK
+
+ FSTEST_EXIT();
+}
+
+static void
+fcntl_lock(const atf_tc_t *tc, const char *mp)
+{
+ int fd, fd2;
+ struct flock l;
+ struct lwp *lwp1, *lwp2;
+
+ FSTEST_ENTER();
+ l.l_pid = 0;
+ l.l_start = l.l_len = 1024;
+ l.l_type = F_RDLCK | F_WRLCK;
+ l.l_whence = SEEK_END;
+
+ lwp1 = rump_pub_lwproc_curlwp();
+ RL(fd = rump_sys_open(TESTFILE, O_RDWR | O_CREAT, 0755));
+ RL(rump_sys_ftruncate(fd, 8192));
+
+ RL(rump_sys_fcntl(fd, F_SETLK, &l));
+
+ /* Next, we fork and try to lock the same area */
+ RZ(rump_pub_lwproc_rfork(RUMP_RFCFDG));
+ lwp2 = rump_pub_lwproc_curlwp();
+ RL(fd2 = rump_sys_open(TESTFILE, O_RDWR, 0));
+ ATF_REQUIRE_ERRNO(EAGAIN, rump_sys_fcntl(fd2, F_SETLK, &l));
+
+ /* Switch back and unlock... */
+ rump_pub_lwproc_switch(lwp1);
+ l.l_type = F_UNLCK;
+ RL(rump_sys_fcntl(fd, F_SETLK, &l));
+
+ /* ... and try to lock again */
+ rump_pub_lwproc_switch(lwp2);
+ l.l_type = F_RDLCK | F_WRLCK;
+ RL(rump_sys_fcntl(fd2, F_SETLK, &l));
+
+ RL(rump_sys_close(fd2));
+ rump_pub_lwproc_releaselwp();
+
+ RL(rump_sys_close(fd));
+
+ FSTEST_EXIT();
+}
+
+static int
+flock_compare(const void *p, const void *q)
+{
+ int a = ((const struct flock *)p)->l_start;
+ int b = ((const struct flock *)q)->l_start;
+ return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+/*
+ * Find all locks set by fcntl_getlock_pids test
+ * using GETLK for a range [start, start+end], and,
+ * if there is a blocking lock, recursively find
+ * all locks to the left (toward the beginning of
+ * a file) and to the right of the lock.
+ * The function also understands "until end of file"
+ * convention when len==0.
+ */
+static unsigned int
+fcntl_getlocks(int fildes, off_t start, off_t len,
+ struct flock *lock, struct flock *end)
+{
+ unsigned int rv = 0;
+ const struct flock l = { start, len, 0, F_RDLCK, SEEK_SET };
+
+ if (lock == end)
+ return rv;
+
+ RL(rump_sys_fcntl(fildes, F_GETLK, &l));
+
+ if (l.l_type == F_UNLCK)
+ return rv;
+
+ *lock++ = l;
+ rv += 1;
+
+ ATF_REQUIRE(l.l_whence == SEEK_SET);
+
+ if (l.l_start > start) {
+ unsigned int n =
+ fcntl_getlocks(fildes, start, l.l_start - start, lock, end);
+ rv += n;
+ lock += n;
+ if (lock == end)
+ return rv;
+ }
+
+ if (l.l_len == 0) /* does l spans until the end? */
+ return rv;
+
+ if (len == 0) /* are we looking for locks until the end? */ {
+ rv += fcntl_getlocks(fildes, l.l_start + l.l_len, len, lock, end);
+ } else if (l.l_start + l.l_len < start + len) {
+ len -= l.l_start + l.l_len - start;
+ rv += fcntl_getlocks(fildes, l.l_start + l.l_len, len, lock, end);
+ }
+
+ return rv;
+}
+
+static void
+fcntl_getlock_pids(const atf_tc_t *tc, const char *mp)
+{
+ /* test non-overlaping ranges */
+ struct flock expect[4];
+ const struct flock lock[4] = {
+ { 0, 2, 0, F_WRLCK, SEEK_SET },
+ { 2, 1, 0, F_WRLCK, SEEK_SET },
+ { 7, 5, 0, F_WRLCK, SEEK_SET },
+ { 4, 3, 0, F_WRLCK, SEEK_SET },
+ };
+
+ /* Add extra element to make sure recursion does't stop at array end */
+ struct flock result[5];
+
+ /* Add 5th process */
+ int fd[5];
+ pid_t pid[5];
+ struct lwp *lwp[5];
+
+ unsigned int i, j;
+ const off_t sz = 8192;
+ int omode = 0755;
+ int oflags = O_RDWR | O_CREAT;
+
+ memcpy(expect, lock, sizeof(lock));
+
+ FSTEST_ENTER();
+
+ /*
+ * First, we create 4 processes and let each lock a range of the
+ * file. Note that the third and fourth processes lock in
+ * "reverse" order, i.e. the greater pid locks a range before
+ * the lesser pid.
+ * Then, we create 5th process which doesn't lock anything.
+ */
+ for (i = 0; i < __arraycount(lwp); i++) {
+ RZ(rump_pub_lwproc_rfork(RUMP_RFCFDG));
+
+ lwp[i] = rump_pub_lwproc_curlwp();
+ pid[i] = rump_sys_getpid();
+
+ RL(fd[i] = rump_sys_open(TESTFILE, oflags, omode));
+ oflags = O_RDWR;
+ omode = 0;
+
+ RL(rump_sys_ftruncate(fd[i], sz));
+
+ if (i < __arraycount(lock)) {
+ RL(rump_sys_fcntl(fd[i], F_SETLK, &lock[i]));
+ expect[i].l_pid = pid[i];
+ }
+ }
+
+ qsort(expect, __arraycount(expect), sizeof(expect[0]), &flock_compare);
+
+ /*
+ * In the context of each process, recursively find all locks
+ * that would block the current process. Processes 1-4 don't
+ * see their own lock, we insert it to simplify checks.
+ * Process 5 sees all 4 locks.
+ */
+ for (i = 0; i < __arraycount(lwp); i++) {
+ unsigned int nlocks;
+
+ rump_pub_lwproc_switch(lwp[i]);
+
+ memset(result, 0, sizeof(result));
+ nlocks = fcntl_getlocks(fd[i], 0, sz,
+ result, result + __arraycount(result));
+
+ if (i < __arraycount(lock)) {
+ ATF_REQUIRE(nlocks < __arraycount(result));
+ result[nlocks] = lock[i];
+ result[nlocks].l_pid = pid[i];
+ nlocks++;
+ }
+
+ ATF_CHECK_EQ(nlocks, __arraycount(expect));
+
+ qsort(result, nlocks, sizeof(result[0]), &flock_compare);
+
+ for (j = 0; j < nlocks; j++) {
+ ATF_CHECK_EQ(result[j].l_start, expect[j].l_start );
+ ATF_CHECK_EQ(result[j].l_len, expect[j].l_len );
+ ATF_CHECK_EQ(result[j].l_pid, expect[j].l_pid );
+ ATF_CHECK_EQ(result[j].l_type, expect[j].l_type );
+ ATF_CHECK_EQ(result[j].l_whence, expect[j].l_whence);
+ }
+ }
+
+ /*
+ * Release processes. This also releases the fds and locks
+ * making fs unmount possible
+ */
+ for (i = 0; i < __arraycount(lwp); i++) {
+ rump_pub_lwproc_switch(lwp[i]);
+ rump_pub_lwproc_releaselwp();
+ }
+
+ FSTEST_EXIT();
+}
+
+static void
+access_simple(const atf_tc_t *tc, const char *mp)
+{
+ int fd;
+ int tmode;
+
+ FSTEST_ENTER();
+ RL(fd = rump_sys_open("tfile", O_CREAT | O_RDWR, 0777));
+ RL(rump_sys_close(fd));
+
+#define ALLACC (F_OK | X_OK | W_OK | R_OK)
+ if (FSTYPE_SYSVBFS(tc) || FSTYPE_MSDOS(tc))
+ tmode = F_OK;
+ else
+ tmode = ALLACC;
+
+ RL(rump_sys_access("tfile", tmode));
+
+ /* PR kern/44648 */
+ ATF_REQUIRE_ERRNO(EINVAL, rump_sys_access("tfile", ALLACC+1) == -1);
+#undef ALLACC
+ FSTEST_EXIT();
+}
+
+static void
+read_directory(const atf_tc_t *tc, const char *mp)
+{
+ char buf[1024];
+ int fd, res;
+ ssize_t size;
+
+ FSTEST_ENTER();
+ fd = rump_sys_open(".", O_DIRECTORY | O_RDONLY, 0777);
+ ATF_REQUIRE(fd != -1);
+
+ size = rump_sys_pread(fd, buf, sizeof(buf), 0);
+ ATF_CHECK(size != -1 || errno == EISDIR);
+ size = rump_sys_read(fd, buf, sizeof(buf));
+ ATF_CHECK(size != -1 || errno == EISDIR);
+
+ res = rump_sys_close(fd);
+ ATF_REQUIRE(res != -1);
+ FSTEST_EXIT();
+}
+
+static void
+lstat_symlink(const atf_tc_t *tc, const char *mp)
+{
+ const char *src, *dst;
+ int res;
+ struct stat st;
+
+ USES_SYMLINKS;
+
+ FSTEST_ENTER();
+
+ src = "source";
+ dst = "destination";
+
+ res = rump_sys_symlink(src, dst);
+ ATF_REQUIRE(res != -1);
+ res = rump_sys_lstat(dst, &st);
+ ATF_REQUIRE(res != -1);
+
+ ATF_CHECK(S_ISLNK(st.st_mode) != 0);
+ ATF_CHECK(st.st_size == (off_t)strlen(src));
+
+ FSTEST_EXIT();
+}
+
+ATF_TC_FSAPPLY(lookup_simple, "simple lookup (./.. on root)");
+ATF_TC_FSAPPLY(lookup_complex, "lookup of non-dot entries");
+ATF_TC_FSAPPLY(dir_simple, "mkdir/rmdir");
+ATF_TC_FSAPPLY(dir_notempty, "non-empty directories cannot be removed");
+ATF_TC_FSAPPLY(dir_rmdirdotdot, "remove .. and try to cd out (PR kern/44657)");
+ATF_TC_FSAPPLY(rename_dir, "exercise various directory renaming ops "
+"(PR kern/44288)");
+ATF_TC_FSAPPLY(rename_dotdot, "rename dir .. (PR kern/43617)");
+ATF_TC_FSAPPLY(rename_reg_nodir, "rename regular files, no subdirectories");
+ATF_TC_FSAPPLY(create_nametoolong, "create file with name too long");
+ATF_TC_FSAPPLY(create_exist, "create with O_EXCL");
+ATF_TC_FSAPPLY(rename_nametoolong, "rename to file with name too long");
+ATF_TC_FSAPPLY(symlink_zerolen, "symlink with target of length 0");
+ATF_TC_FSAPPLY(symlink_long, "symlink with target of length > 0");
+ATF_TC_FSAPPLY(symlink_root, "symlink to root directory");
+ATF_TC_FSAPPLY(attrs, "check setting attributes works");
+ATF_TC_FSAPPLY(fcntl_lock, "check fcntl F_SETLK");
+ATF_TC_FSAPPLY(fcntl_getlock_pids,"fcntl F_GETLK w/ many procs, PR kern/44494");
+ATF_TC_FSAPPLY(access_simple, "access(2)");
+ATF_TC_FSAPPLY(read_directory, "read(2) on directories");
+ATF_TC_FSAPPLY(lstat_symlink, "lstat(2) values for symbolic links");
+
+#undef FSTEST_IMGSIZE
+#define FSTEST_IMGSIZE (1024*1024*64)
+ATF_TC_FSAPPLY(create_many, "create many directory entries");
+ATF_TC_FSAPPLY(create_nonalphanum, "non-alphanumeric filenames");
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_FSAPPLY(lookup_simple);
+ ATF_TP_FSAPPLY(lookup_complex);
+ ATF_TP_FSAPPLY(dir_simple);
+ ATF_TP_FSAPPLY(dir_notempty);
+ ATF_TP_FSAPPLY(dir_rmdirdotdot);
+ ATF_TP_FSAPPLY(rename_dir);
+ ATF_TP_FSAPPLY(rename_dotdot);
+ ATF_TP_FSAPPLY(rename_reg_nodir);
+ ATF_TP_FSAPPLY(create_many);
+ ATF_TP_FSAPPLY(create_nonalphanum);
+ ATF_TP_FSAPPLY(create_nametoolong);
+ ATF_TP_FSAPPLY(create_exist);
+ ATF_TP_FSAPPLY(rename_nametoolong);
+ ATF_TP_FSAPPLY(symlink_zerolen);
+ ATF_TP_FSAPPLY(symlink_long);
+ ATF_TP_FSAPPLY(symlink_root);
+ ATF_TP_FSAPPLY(attrs);
+ ATF_TP_FSAPPLY(fcntl_lock);
+ ATF_TP_FSAPPLY(fcntl_getlock_pids);
+ ATF_TP_FSAPPLY(access_simple);
+ ATF_TP_FSAPPLY(read_directory);
+ ATF_TP_FSAPPLY(lstat_symlink);
+
+ return atf_no_error();
+}
diff --git a/contrib/netbsd-tests/fs/zfs/t_zpool.sh b/contrib/netbsd-tests/fs/zfs/t_zpool.sh
new file mode 100755
index 000000000000..e3c2380da343
--- /dev/null
+++ b/contrib/netbsd-tests/fs/zfs/t_zpool.sh
@@ -0,0 +1,66 @@
+# $NetBSD: t_zpool.sh,v 1.3 2011/12/06 18:18:59 njoly Exp $
+#
+# Copyright (c) 2011 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+server='rump_server -lrumpvfs -lrumpkern_solaris -lrumpfs_zfs -lrumpdev -lrumpdev_rnd -d key=/dk,hostpath=zfs.img,size=100m'
+
+export RUMP_SERVER=unix://zsuck
+
+atf_test_case create cleanup
+create_head()
+{
+ atf_set "descr" "basic zpool create"
+}
+
+IFS=' '
+exmount='rumpfs on / type rumpfs (local)
+jippo on /jippo type zfs (local)
+'
+
+create_body()
+{
+
+ atf_check -s exit:0 -o ignore -e ignore ${server} ${RUMP_SERVER}
+
+ export LD_PRELOAD=/usr/lib/librumphijack.so
+ export RUMPHIJACK=blanket=/dev/zfs:/dk:/jippo
+ atf_check -s exit:0 zpool create jippo /dk
+
+ export RUMPHIJACK=vfs=all
+ atf_check -s exit:0 -o inline:"${exmount}" mount
+}
+
+create_cleanup()
+{
+
+ rump.halt
+}
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case create
+}