From e535bfc7e483c14fe883d5c1b4e38baa1cab160e Mon Sep 17 00:00:00 2001 From: "Hudson C. Dalpra" Date: Tue, 3 Mar 2026 20:49:56 +1300 Subject: [PATCH 1/2] miscfs/fullfs: add disk-full simulation layerfs based on miscfs/nullfs --- distrib/sets/lists/base/mi | 2 + distrib/sets/lists/base/shl.mi | 3 + distrib/sets/lists/comp/mi | 3 + distrib/sets/lists/debug/mi | 2 + distrib/sets/lists/debug/module.mi | 2 + distrib/sets/lists/man/mi | 2 + distrib/sets/lists/manhtml/mi | 1 + distrib/sets/lists/modules/mi | 2 + distrib/sets/lists/tests/mi | 5 + etc/mtree/NetBSD.dist.tests | 2 + sbin/Makefile | 4 + sbin/mount_full/Makefile | 16 ++ sbin/mount_full/mount_full.8 | 151 +++++++++++++++ sbin/mount_full/mount_full.c | 127 +++++++++++++ share/man/man5/mk.conf.5 | 11 ++ share/mk/bsd.README | 2 +- share/mk/bsd.own.mk | 2 + sys/conf/files | 1 + sys/miscfs/Makefile | 2 +- sys/miscfs/fullfs/Makefile | 7 + sys/miscfs/fullfs/files.fullfs | 7 + sys/miscfs/fullfs/full.h | 115 ++++++++++++ sys/miscfs/fullfs/full_vfsops.c | 285 +++++++++++++++++++++++++++++ sys/miscfs/fullfs/full_vnops.c | 143 +++++++++++++++ sys/modules/Makefile | 1 + sys/modules/full/Makefile | 12 ++ sys/rump/fs/Makefile.rumpfscomp | 4 +- sys/rump/fs/lib/libfull/Makefile | 13 ++ sys/sys/mount.h | 1 + sys/sys/vnode.h | 4 +- tests/fs/Makefile | 4 + tests/fs/fullfs/Makefile | 13 ++ tests/fs/fullfs/t_basic.c | 203 ++++++++++++++++++++ 33 files changed, 1146 insertions(+), 6 deletions(-) create mode 100644 sbin/mount_full/Makefile create mode 100644 sbin/mount_full/mount_full.8 create mode 100644 sbin/mount_full/mount_full.c create mode 100644 sys/miscfs/fullfs/Makefile create mode 100644 sys/miscfs/fullfs/files.fullfs create mode 100644 sys/miscfs/fullfs/full.h create mode 100644 sys/miscfs/fullfs/full_vfsops.c create mode 100644 sys/miscfs/fullfs/full_vnops.c create mode 100644 sys/modules/full/Makefile create mode 100644 sys/rump/fs/lib/libfull/Makefile create mode 100644 tests/fs/fullfs/Makefile create mode 100644 tests/fs/fullfs/t_basic.c diff --git a/distrib/sets/lists/base/mi b/distrib/sets/lists/base/mi index 45f64fdcc2bc0..968d2ff92965b 100644 --- a/distrib/sets/lists/base/mi +++ b/distrib/sets/lists/base/mi @@ -420,6 +420,7 @@ ./sbin/mount_fdesc base-miscfs-root fdescfs ./sbin/mount_ffs base-sysutil-root ./sbin/mount_filecore base-filecorefs-root filecorefs +./sbin/mount_full base-miscfs-root fullfs ./sbin/mount_hfs base-hfs-root hfs ./sbin/mount_hfsp base-obsolete obsolete ./sbin/mount_kernfs base-sysutil-root kernfs @@ -1128,6 +1129,7 @@ ./usr/include/miscfs base-c-usr ./usr/include/miscfs/fdesc base-c-usr ./usr/include/miscfs/fifofs base-c-usr +./usr/include/miscfs/fullfs base-c-usr ./usr/include/miscfs/genfs base-c-usr ./usr/include/miscfs/kernfs base-c-usr ./usr/include/miscfs/nullfs base-c-usr diff --git a/distrib/sets/lists/base/shl.mi b/distrib/sets/lists/base/shl.mi index 996063a2d489a..7abf7b43c6756 100644 --- a/distrib/sets/lists/base/shl.mi +++ b/distrib/sets/lists/base/shl.mi @@ -677,6 +677,9 @@ ./usr/lib/librumpfs_ffs.so base-rump-shlib rump ./usr/lib/librumpfs_ffs.so.0 base-rump-shlib rump ./usr/lib/librumpfs_ffs.so.0.0 base-rump-shlib rump +./usr/lib/librumpfs_full.so base-rump-shlib rump +./usr/lib/librumpfs_full.so.0 base-rump-shlib rump +./usr/lib/librumpfs_full.so.0.0 base-rump-shlib rump ./usr/lib/librumpfs_hfs.so base-rump-shlib rump ./usr/lib/librumpfs_hfs.so.0 base-rump-shlib rump ./usr/lib/librumpfs_hfs.so.0.0 base-rump-shlib rump diff --git a/distrib/sets/lists/comp/mi b/distrib/sets/lists/comp/mi index 08f5cc1e45bd5..58b2d76d0b07d 100644 --- a/distrib/sets/lists/comp/mi +++ b/distrib/sets/lists/comp/mi @@ -2787,6 +2787,7 @@ ./usr/include/milter/mfdef.h comp-obsolete obsolete ./usr/include/miscfs/fdesc/fdesc.h comp-c-include ./usr/include/miscfs/fifofs/fifo.h comp-c-include +./usr/include/miscfs/fullfs/full.h comp-c-include ./usr/include/miscfs/genfs/genfs.h comp-c-include ./usr/include/miscfs/genfs/genfs_node.h comp-c-include ./usr/include/miscfs/genfs/layer.h comp-c-include @@ -4339,6 +4340,8 @@ ./usr/lib/librumpfs_fdesc_p.a comp-c-proflib rump,profile ./usr/lib/librumpfs_ffs.a comp-c-lib rump ./usr/lib/librumpfs_ffs_p.a comp-c-proflib rump,profile +./usr/lib/librumpfs_full.a comp-c-lib rump,fullfs +./usr/lib/librumpfs_full_p.a comp-c-proflib rump,fullfs,profile ./usr/lib/librumpfs_hfs.a comp-c-lib rump ./usr/lib/librumpfs_hfs_p.a comp-c-proflib rump,profile ./usr/lib/librumpfs_kernfs.a comp-c-lib rump diff --git a/distrib/sets/lists/debug/mi b/distrib/sets/lists/debug/mi index 4f4a5d45c89e3..69b49c5df5c23 100644 --- a/distrib/sets/lists/debug/mi +++ b/distrib/sets/lists/debug/mi @@ -391,6 +391,7 @@ ./usr/libdata/debug/sbin/mount_fdesc.debug comp-miscfs-debug fdescfs,debug ./usr/libdata/debug/sbin/mount_ffs.debug comp-sysutil-debug debug ./usr/libdata/debug/sbin/mount_filecore.debug comp-filecorefs-debug filecorefs,debug +./usr/libdata/debug/sbin/mount_full.debug comp-miscfs-debug fullfs,debug ./usr/libdata/debug/sbin/mount_hfs.debug comp-hfs-debug hfs,debug ./usr/libdata/debug/sbin/mount_kernfs.debug comp-sysutil-debug kernfs,debug ./usr/libdata/debug/sbin/mount_lfs.debug comp-sysutil-debug lfs,debug @@ -1714,6 +1715,7 @@ ./usr/libdata/debug/usr/tests/fs/ffs/t_snapshot_v2.debug tests-fs-debug debug,atf,rump ./usr/libdata/debug/usr/tests/fs/ffs/t_update_log.debug tests-fs-debug debug,atf,rump ./usr/libdata/debug/usr/tests/fs/fifofs/t_fifo.debug tests-fs-debug debug,atf,rump +./usr/libdata/debug/usr/tests/fs/fullfs/t_basic.debug tests-fs-debug fullfs,debug,atf,rump ./usr/libdata/debug/usr/tests/fs/hfs/t_pathconvert.debug tests-fs-debug hfs,debug,atf,rump ./usr/libdata/debug/usr/tests/fs/kernfs/t_basic.debug tests-fs-debug kernfs,debug,atf,rump ./usr/libdata/debug/usr/tests/fs/lfs/t_basic.debug tests-fs-debug lfs,debug,atf,rump diff --git a/distrib/sets/lists/debug/module.mi b/distrib/sets/lists/debug/module.mi index 7669d22f21403..d87d2bf13eaa5 100644 --- a/distrib/sets/lists/debug/module.mi +++ b/distrib/sets/lists/debug/module.mi @@ -163,6 +163,8 @@ ./usr/libdata/debug/@MODULEDIR@/flash/flash.kmod.debug modules-base-kernel kmod,debug ./usr/libdata/debug/@MODULEDIR@/fss modules-base-kernel kmod,debug ./usr/libdata/debug/@MODULEDIR@/fss/fss.kmod.debug modules-base-kernel kmod,debug +./usr/libdata/debug/@MODULEDIR@/full modules-base-kernel kmod,debug +./usr/libdata/debug/@MODULEDIR@/full/full.kmod.debug modules-base-kernel kmod,debug ./usr/libdata/debug/@MODULEDIR@/gpio modules-base-kernel kmod,debug ./usr/libdata/debug/@MODULEDIR@/gpio/gpio.kmod.debug modules-base-kernel kmod,debug ./usr/libdata/debug/@MODULEDIR@/gpioiic modules-base-kernel kmod,debug diff --git a/distrib/sets/lists/man/mi b/distrib/sets/lists/man/mi index 593bd2130c8b9..70e39302435fe 100644 --- a/distrib/sets/lists/man/mi +++ b/distrib/sets/lists/man/mi @@ -3100,6 +3100,7 @@ ./usr/share/man/cat8/mount_fdesc.0 man-miscfs-catman fdescfs,.cat ./usr/share/man/cat8/mount_ffs.0 man-sysutil-catman .cat ./usr/share/man/cat8/mount_filecore.0 man-filecorefs-catman filecorefs,.cat +./usr/share/man/cat8/mount_full.0 man-miscfs-catman fullfs,.cat ./usr/share/man/cat8/mount_hfs.0 man-hfs-catman hfs,.cat ./usr/share/man/cat8/mount_hfsp.0 man-obsolete obsolete ./usr/share/man/cat8/mount_kernfs.0 man-sysutil-catman kernfs,.cat @@ -6693,6 +6694,7 @@ ./usr/share/man/man8/mount_fdesc.8 man-miscfs-man fdescfs,.man ./usr/share/man/man8/mount_ffs.8 man-sysutil-man .man ./usr/share/man/man8/mount_filecore.8 man-filecorefs-man filecorefs,.man +./usr/share/man/man8/mount_full.8 man-miscfs-man fullfs,.man ./usr/share/man/man8/mount_hfs.8 man-hfs-man hfs,.man ./usr/share/man/man8/mount_hfsp.8 man-obsolete obsolete ./usr/share/man/man8/mount_kernfs.8 man-sysutil-man kernfs,.man diff --git a/distrib/sets/lists/manhtml/mi b/distrib/sets/lists/manhtml/mi index 03ab42791f01a..4c30d403c0c69 100644 --- a/distrib/sets/lists/manhtml/mi +++ b/distrib/sets/lists/manhtml/mi @@ -2776,6 +2776,7 @@ ./usr/share/man/html8/mount_fdesc.html man-miscfs-htmlman fdescfs,html ./usr/share/man/html8/mount_ffs.html man-sysutil-htmlman html ./usr/share/man/html8/mount_filecore.html man-filecorefs-htmlman filecorefs,html +./usr/share/man/html8/mount_full.html man-miscfs-htmlman fullfs,html ./usr/share/man/html8/mount_hfs.html man-hfs-htmlman hfs,html ./usr/share/man/html8/mount_kernfs.html man-sysutil-htmlman kernfs,html ./usr/share/man/html8/mount_lfs.html man-sysutil-htmlman lfs,html diff --git a/distrib/sets/lists/modules/mi b/distrib/sets/lists/modules/mi index 115be941ef341..086662f215413 100644 --- a/distrib/sets/lists/modules/mi +++ b/distrib/sets/lists/modules/mi @@ -185,6 +185,8 @@ ./@MODULEDIR@/flash/flash.kmod modules-base-kernel kmod ./@MODULEDIR@/fss modules-base-kernel kmod ./@MODULEDIR@/fss/fss.kmod modules-base-kernel kmod +./@MODULEDIR@/full modules-base-kernel kmod +./@MODULEDIR@/full/full.kmod modules-base-kernel kmod ./@MODULEDIR@/gpio modules-base-kernel kmod ./@MODULEDIR@/gpio/gpio.kmod modules-base-kernel kmod ./@MODULEDIR@/gpioiic modules-base-kernel kmod diff --git a/distrib/sets/lists/tests/mi b/distrib/sets/lists/tests/mi index 52f831dc46aa1..cfe071df278e2 100644 --- a/distrib/sets/lists/tests/mi +++ b/distrib/sets/lists/tests/mi @@ -37,6 +37,7 @@ ./usr/libdata/debug/usr/tests/fs/cd9660 tests-fs-debug compattestfile,atf ./usr/libdata/debug/usr/tests/fs/ffs tests-fs-debug compattestfile,atf ./usr/libdata/debug/usr/tests/fs/fifofs tests-fs-debug compattestfile,atf +./usr/libdata/debug/usr/tests/fs/fullfs tests-fs-debug compattestfile,atf ./usr/libdata/debug/usr/tests/fs/hfs tests-fs-debug compattestfile,atf ./usr/libdata/debug/usr/tests/fs/kernfs tests-fs-debug compattestfile,atf ./usr/libdata/debug/usr/tests/fs/lfs tests-fs-debug compattestfile,atf @@ -1682,6 +1683,10 @@ ./usr/tests/fs/fifofs/Atffile tests-fs-tests atf,rump ./usr/tests/fs/fifofs/Kyuafile tests-fs-tests atf,rump,kyua ./usr/tests/fs/fifofs/t_fifo tests-fs-tests atf,rump +./usr/tests/fs/fullfs tests-fs-tests compattestfile,atf +./usr/tests/fs/fullfs/Atffile tests-fs-tests atf,rump +./usr/tests/fs/fullfs/Kyuafile tests-fs-tests atf,rump,kyua +./usr/tests/fs/fullfs/t_basic tests-fs-tests atf,rump ./usr/tests/fs/h_funcs.subr tests-fs-tests atf,rump ./usr/tests/fs/hfs tests-fs-tests compattestfile,atf ./usr/tests/fs/hfs/Atffile tests-fs-tests atf,rump diff --git a/etc/mtree/NetBSD.dist.tests b/etc/mtree/NetBSD.dist.tests index 919e17cb978b3..40f7c826fd4ea 100644 --- a/etc/mtree/NetBSD.dist.tests +++ b/etc/mtree/NetBSD.dist.tests @@ -29,6 +29,7 @@ ./usr/libdata/debug/usr/tests/fs/cd9660 ./usr/libdata/debug/usr/tests/fs/ffs ./usr/libdata/debug/usr/tests/fs/fifofs +./usr/libdata/debug/usr/tests/fs/fullfs ./usr/libdata/debug/usr/tests/fs/hfs ./usr/libdata/debug/usr/tests/fs/kernfs ./usr/libdata/debug/usr/tests/fs/lfs @@ -235,6 +236,7 @@ ./usr/tests/fs/cd9660 ./usr/tests/fs/ffs ./usr/tests/fs/fifofs +./usr/tests/fs/fullfs ./usr/tests/fs/hfs ./usr/tests/fs/kernfs ./usr/tests/fs/lfs diff --git a/sbin/Makefile b/sbin/Makefile index 7a5c776841e06..8ad2bbb96ee27 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -92,6 +92,10 @@ SUBDIR+= mount_ntfs SUBDIR+= mount_null .endif +.if (${MKFULLFS} != "no") +SUBDIR+= mount_full +.endif + .if (${MKOVERLAYFS} != "no") SUBDIR+= mount_overlay .endif diff --git a/sbin/mount_full/Makefile b/sbin/mount_full/Makefile new file mode 100644 index 0000000000000..978e0ddf6999d --- /dev/null +++ b/sbin/mount_full/Makefile @@ -0,0 +1,16 @@ +# $NetBSD$ + +.include + +PROG= mount_full +SRCS= mount_full.c pathadj.c +MAN= mount_full.8 + +MOUNT= ${NETBSDSRCDIR}/sbin/mount +CPPFLAGS+= -I${NETBSDSRCDIR}/sys -I${MOUNT} +.PATH: ${MOUNT} + +DPADD+=${LIBUTIL} +LDADD+=-lutil + +.include diff --git a/sbin/mount_full/mount_full.8 b/sbin/mount_full/mount_full.8 new file mode 100644 index 0000000000000..81f29b86b2305 --- /dev/null +++ b/sbin/mount_full/mount_full.8 @@ -0,0 +1,151 @@ +.\" $NetBSD$ +.\" +.\" Copyright (c) 1992, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software donated to Berkeley by +.\" Jan-Simon Pendry. +.\" +.\" 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. +.\" +.Dd March 6, 2026 +.Dt MOUNT_FULL 8 +.Os +.Sh NAME +.Nm mount_full +.Nd mount a disk-full simulation filesystem layer +.Sh SYNOPSIS +.Nm +.Op Fl o Ar options +.Ar target +.Ar mount-point +.Sh DESCRIPTION +The +.Nm +command mounts a +.Em fullfs +layer over +.Ar target , +making it accessible at +.Ar mount-point . +.Pp +.Em fullfs +is a stackable filesystem layer built on the same layerfs infrastructure as +.Xr mount_null 8 . +It presents the contents of +.Ar target +unchanged, but intercepts any vnode operation that would allocate disk space +and returns +.Er ENOSPC +immediately, without touching the underlying filesystem. +.Pp +This makes it possible to test an application's handling of disk-full +conditions without filling an actual disk. +.Pp +Both +.Ar target +and +.Ar mount-point +are converted to absolute paths before use. +.Sh BEHAVIOR +The following operations always fail with +.Er ENOSPC +through a fullfs mount: +.Bl -bullet -compact +.It +.Xr write 2 +.It +.Xr open 2 +with +.Dv O_CREAT +(new file creation) +.It +.Xr mkdir 2 +.It +.Xr mknod 2 +.It +.Xr symlink 2 +.It +.Xr link 2 +.El +.Pp +The following operations pass through to the underlying filesystem unchanged: +.Bl -bullet -compact +.It +.Xr read 2 , +.Xr readdir 2 , +.Xr stat 2 , +and other read-only operations +.It +.Xr unlink 2 +and +.Xr rmdir 2 +(these free space rather than allocating it) +.It +.Xr rename 2 +.El +.Pp +.Xr statvfs 2 +reports zero free and available blocks and inodes, regardless of the +actual state of the underlying filesystem. +.Sh OPTIONS +.Bl -tag -width Fl +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma-separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.El +.Sh EXAMPLES +Mount +.Pa /var/db +under +.Pa /mnt/full +to test how a program behaves when the database volume is full: +.Pp +.Dl mount_full /var/db /mnt/full +.Pp +Verify that the layer is working correctly: +.Bd -literal -offset indent +# Existing data is still readable: +ls /mnt/full + +# Writes fail immediately: +echo hello >> /mnt/full/somefile +echo $? # non-zero; "No space left on device" + +# Free space is reported as zero: +df /mnt/full +.Ed +.Sh SEE ALSO +.Xr mount 8 , +.Xr mount_null 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Nx 11 . diff --git a/sbin/mount_full/mount_full.c b/sbin/mount_full/mount_full.c new file mode 100644 index 0000000000000..a3a851a28c2e8 --- /dev/null +++ b/sbin/mount_full/mount_full.c @@ -0,0 +1,127 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * 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 +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1992, 1993, 1994\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mount_full.c 8.6 (Berkeley) 4/26/95"; +#else +__RCSID("$NetBSD$"); +#endif +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "mountprog.h" + +static const struct mntopt mopts[] = { + MOPT_STDOPTS, + MOPT_GETARGS, + MOPT_NULL, +}; + +int mount_full(int argc, char **argv); +__dead static void usage(void); + +#ifndef MOUNT_NOMAIN +int +main(int argc, char **argv) +{ + return mount_full(argc, argv); +} +#endif + +int +mount_full(int argc, char *argv[]) +{ + struct full_args args; + int ch, mntflags; + char target[MAXPATHLEN], canon_dir[MAXPATHLEN]; + mntoptparse_t mp; + + mntflags = 0; + while ((ch = getopt(argc, argv, "o:")) != -1) + switch(ch) { + case 'o': + mp = getmntopts(optarg, mopts, &mntflags, 0); + if (mp == NULL) + err(1, "getmntopts"); + freemntopts(mp); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + pathadj(argv[0], target); + pathadj(argv[1], canon_dir); + + if (strcmp(target, canon_dir) == 0) + errx(1, "%s (%s) and %s (%s) are identical paths", + argv[0], target, argv[1], canon_dir); + + args.la.target = target; + + if (mount(MOUNT_FULL, canon_dir, mntflags, &args, sizeof args) == -1) + err(1, "%s on %s", target, canon_dir); + exit(0); +} + +static void +usage(void) +{ + (void)fprintf(stderr, + "usage: mount_full [-o options] target_fs mount_point\n"); + exit(1); +} diff --git a/share/man/man5/mk.conf.5 b/share/man/man5/mk.conf.5 index a58b9435f0a4d..07d7439197c5c 100644 --- a/share/man/man5/mk.conf.5 +++ b/share/man/man5/mk.conf.5 @@ -1024,6 +1024,17 @@ and .Dq no on other platforms. . +.It Sy MKFULLFS +.YorN +Indicates whether support for the FULLFS disk-full +simulation file system will be built and installed. +.DFLT +.Dq no +on +.Sy m68000 ; +.Dq yes +on other platforms. +. .It Sy MKGCC .YorN Indicates whether diff --git a/share/mk/bsd.README b/share/mk/bsd.README index d30029a26d1af..dcc074de99fb3 100644 --- a/share/mk/bsd.README +++ b/share/mk/bsd.README @@ -117,7 +117,7 @@ The supported mk.conf(5) make variables are: MKCOMPATMODULES, MKCOMPATTESTS, MKCOMPATX11, MKCOMPLEX, MKCROSSGDB, MKCTF, MKCVS, MKCXX, MKDEBUG, MKDEBUGKERNEL, MKDEBUGLIB, MKDEBUGTOOLS, MKDEPINCLUDES, MKDOC, MKDTB, MKDTC, MKDTRACE, MKDYNAMICROOT, MKEFS, - MKEXT2FS, MKFDESCFS, MKFIDO2, MKFILECOREFS, MKFIRMWARE, MKGCC, + MKEXT2FS, MKFDESCFS, MKFIDO2, MKFILECOREFS, MKFIRMWARE, MKFULLFS, MKGCC, MKGCCCMDS, MKGDB, MKGROFF, MKGROFFHTMLDOC, MKHESIOD, MKHFS, MKKERNFS, MKHOSTOBJ, MKHTML, MKIEEEFP, MKINET6, MKINFO, MKIPFILTER, MKIPSEC, MKISCSI, MKKERBEROS, MKKMOD, MKKYUA, MKLDAP, MKLFS, MKLIBCSANITIZER, diff --git a/share/mk/bsd.own.mk b/share/mk/bsd.own.mk index 997a26786609a..c419a08ef4884 100644 --- a/share/mk/bsd.own.mk +++ b/share/mk/bsd.own.mk @@ -1303,6 +1303,7 @@ _MKVARS.yes= \ MKFDESCFS \ MKFIDO2 \ MKFILECOREFS \ + MKFULLFS \ MKGCC \ MKGDB \ MKGROFF \ @@ -1381,6 +1382,7 @@ MKEXT2FS.m68000?= no MKFDESCFS.m68000?= no MKFIDO2.m68000?= no MKFILECOREFS.m68000?= no +MKFULLFS.m68000?= no MKGROFF.m68000?= no MKHESIOD.m68000?= no MKHFS.m68000?= no diff --git a/sys/conf/files b/sys/conf/files index 9e8bd63e28efd..d0712df3b37b7 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1586,6 +1586,7 @@ include "fs/autofs/files.autofs" include "miscfs/fdesc/files.fdesc" include "miscfs/kernfs/files.kernfs" include "miscfs/nullfs/files.nullfs" +include "miscfs/fullfs/files.fullfs" include "miscfs/overlay/files.overlay" include "miscfs/procfs/files.procfs" include "miscfs/umapfs/files.umapfs" diff --git a/sys/miscfs/Makefile b/sys/miscfs/Makefile index 9ba12575c96c2..61f6c9ff3f084 100644 --- a/sys/miscfs/Makefile +++ b/sys/miscfs/Makefile @@ -1,6 +1,6 @@ # $NetBSD: Makefile,v 1.10 2015/05/06 15:57:08 hannken Exp $ -SUBDIR= fdesc fifofs genfs kernfs nullfs overlay procfs specfs umapfs +SUBDIR= fdesc fifofs fullfs genfs kernfs nullfs overlay procfs specfs umapfs INCSDIR= /usr/include/miscfs diff --git a/sys/miscfs/fullfs/Makefile b/sys/miscfs/fullfs/Makefile new file mode 100644 index 0000000000000..f2f65ec0b3e2b --- /dev/null +++ b/sys/miscfs/fullfs/Makefile @@ -0,0 +1,7 @@ +# $NetBSD$ + +INCSDIR= /usr/include/miscfs/fullfs + +INCS= full.h + +.include diff --git a/sys/miscfs/fullfs/files.fullfs b/sys/miscfs/fullfs/files.fullfs new file mode 100644 index 0000000000000..68c2036fb7310 --- /dev/null +++ b/sys/miscfs/fullfs/files.fullfs @@ -0,0 +1,7 @@ +# $NetBSD$ + +deffs FULLFS + +define fullfs: vfs, layerfs +file miscfs/fullfs/full_vfsops.c fullfs +file miscfs/fullfs/full_vnops.c fullfs diff --git a/sys/miscfs/fullfs/full.h b/sys/miscfs/fullfs/full.h new file mode 100644 index 0000000000000..99a7019cf2407 --- /dev/null +++ b/sys/miscfs/fullfs/full.h @@ -0,0 +1,115 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1999 National Aeronautics & Space Administration + * All rights reserved. + * + * This software was written by William Studenmund of the + * Numerical Aerospace Simulation Facility, NASA Ames Research Center. + * + * 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 National Aeronautics & Space Administration + * 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 NATIONAL AERONAUTICS & SPACE ADMINISTRATION + * ``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 ADMINISTRATION OR CONTRIB- + * UTORS 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. + */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * 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. + * + * from: Id: lofs.h,v 1.8 1992/05/30 10:05:43 jsp Exp + * @(#)null.h 8.2 (Berkeley) 1/21/94 + */ + +#ifndef _MISCFS_FULLFS_H_ +#define _MISCFS_FULLFS_H_ + +#include + +struct full_args { + struct layer_args la; /* generic layerfs args */ +}; +#define fulla_target la.target +#define fulla_export la.export + +#ifdef _KERNEL +struct full_mount { + struct layer_mount lm; /* generic layerfs mount stuff */ +}; +#define fullm_rootvp lm.layerm_rootvp +#define fullm_export lm.layerm_export +#define fullm_flags lm.layerm_flags +#define fullm_size lm.layerm_size +#define fullm_tag lm.layerm_tag +#define fullm_bypass lm.layerm_bypass +#define fullm_alloc lm.layerm_alloc +#define fullm_vnodeop_p lm.layerm_vnodeop_p +#define fullm_node_hashtbl lm.layerm_node_hashtbl +#define fullm_node_hash lm.layerm_node_hash +#define fullm_hashlock lm.layerm_hashlock + +/* + * A cache of vnode references + */ +struct full_node { + struct layer_node ln; +}; +#define full_hash ln.layer_hash +#define full_lowervp ln.layer_lowervp +#define full_vnode ln.layer_vnode +#define full_flags ln.layer_flags + +#define MOUNTTOFULLMOUNT(mp) ((struct full_mount *)((mp)->mnt_data)) + +extern int (**full_vnodeop_p)(void *); +extern struct vfsops fullfs_vfsops; + +#endif /* _KERNEL */ +#endif /* _MISCFS_FULLFS_H_ */ diff --git a/sys/miscfs/fullfs/full_vfsops.c b/sys/miscfs/fullfs/full_vfsops.c new file mode 100644 index 0000000000000..153565ff7bda1 --- /dev/null +++ b/sys/miscfs/fullfs/full_vfsops.c @@ -0,0 +1,285 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1999 National Aeronautics & Space Administration + * All rights reserved. + * + * This software was written by William Studenmund of the + * Numerical Aerospace Simulation Facility, NASA Ames Research Center. + * + * 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 National Aeronautics & Space Administration + * 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 NATIONAL AERONAUTICS & SPACE ADMINISTRATION + * ``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 ADMINISTRATION OR CONTRIB- + * UTORS 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. + */ + +/* + * Copyright (c) 1992, 1993, 1995 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * 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. + * + * from: Id: lofs_vfsops.c,v 1.9 1992/05/30 10:26:24 jsp Exp + * from: @(#)lofs_vfsops.c 1.2 (Berkeley) 6/18/92 + * @(#)null_vfsops.c 8.7 (Berkeley) 5/14/95 + */ + +/* + * Full file-system: VFS operations. + * + * See full_vnops.c for a description. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD$"); + +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE(MODULE_CLASS_VFS, full, "layerfs"); + +VFS_PROTOS(fullfs); + +int +fullfs_mount(struct mount *mp, const char *path, void *data, size_t *data_len) +{ + struct vnode *lowerrootvp, *vp; + struct full_args *args = data; + struct full_mount *nmp; + struct layer_mount *lmp; + struct pathbuf *pb; + struct nameidata nd; + int error; + + if (args == NULL) + return EINVAL; + if (*data_len < sizeof(*args)) + return EINVAL; + + if (mp->mnt_flag & MNT_GETARGS) { + lmp = MOUNTTOLAYERMOUNT(mp); + if (lmp == NULL) + return EIO; + args->la.target = NULL; + *data_len = sizeof(*args); + return 0; + } + + /* Update is not supported. */ + if (mp->mnt_flag & MNT_UPDATE) + return EOPNOTSUPP; + + /* Find the lower vnode and lock it. */ + error = pathbuf_copyin(args->la.target, &pb); + if (error) { + return error; + } + NDINIT(&nd, LOOKUP, FOLLOW|LOCKLEAF, pb); + if ((error = namei(&nd)) != 0) { + pathbuf_destroy(pb); + return error; + } + lowerrootvp = nd.ni_vp; + pathbuf_destroy(pb); + + /* Create the mount point. */ + nmp = kmem_zalloc(sizeof(struct full_mount), KM_SLEEP); + mp->mnt_data = nmp; + mp->mnt_iflag |= lowerrootvp->v_mount->mnt_iflag & IMNT_MPSAFE; + mp->mnt_iflag |= lowerrootvp->v_mount->mnt_iflag & IMNT_SHRLOOKUP; + + /* + * Make sure that the mount point is sufficiently initialized + * that the node create call will work. + */ + vfs_getnewfsid(mp); + error = vfs_set_lowermount(mp, lowerrootvp->v_mount); + if (error) { + vput(lowerrootvp); + kmem_free(nmp, sizeof(struct full_mount)); + return error; + } + + nmp->fullm_size = sizeof(struct full_node); + nmp->fullm_tag = VT_FULL; + nmp->fullm_bypass = layer_bypass; + nmp->fullm_vnodeop_p = full_vnodeop_p; + + /* Setup a full node for root vnode. */ + VOP_UNLOCK(lowerrootvp); + error = layer_node_create(mp, lowerrootvp, &vp); + if (error) { + vrele(lowerrootvp); + kmem_free(nmp, sizeof(struct full_mount)); + return error; + } + /* + * Keep a held reference to the root vnode. It will be released on + * umount. Note: fullfs is MP-safe. + */ + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + vp->v_vflag |= VV_ROOT; + nmp->fullm_rootvp = vp; + VOP_UNLOCK(vp); + + error = set_statvfs_info(path, UIO_USERSPACE, args->la.target, + UIO_USERSPACE, mp->mnt_op->vfs_name, mp, curlwp); + if (error) + return error; + + if (mp->mnt_lower->mnt_flag & MNT_LOCAL) + mp->mnt_flag |= MNT_LOCAL; + return 0; +} + +int +fullfs_unmount(struct mount *mp, int mntflags) +{ + struct full_mount *nmp = MOUNTTOFULLMOUNT(mp); + struct vnode *full_rootvp = nmp->fullm_rootvp; + int error, flags = 0; + + if (mntflags & MNT_FORCE) + flags |= FORCECLOSE; + + if (vrefcnt(full_rootvp) > 1 && (mntflags & MNT_FORCE) == 0) + return EBUSY; + + if ((error = vflush(mp, full_rootvp, flags)) != 0) + return error; + + /* Eliminate all activity and release the vnode. */ + vgone(full_rootvp); + + /* Finally, destroy the mount point structures. */ + kmem_free(mp->mnt_data, sizeof(struct full_mount)); + mp->mnt_data = NULL; + return 0; +} + +int +fullfs_statvfs(struct mount *mp, struct statvfs *sbp) +{ + int error; + + error = layerfs_statvfs(mp, sbp); + if (error) + return error; + + /* A full filesystem has no free space. */ + sbp->f_bfree = 0; + sbp->f_bavail = 0; + sbp->f_bresvd = 0; + sbp->f_ffree = 0; + sbp->f_favail = 0; + sbp->f_fresvd = 0; + + return 0; +} + +extern const struct vnodeopv_desc full_vnodeop_opv_desc; + +const struct vnodeopv_desc * const fullfs_vnodeopv_descs[] = { + &full_vnodeop_opv_desc, + NULL, +}; + +struct vfsops fullfs_vfsops = { + .vfs_name = MOUNT_FULL, + .vfs_min_mount_data = sizeof (struct full_args), + .vfs_mount = fullfs_mount, + .vfs_start = layerfs_start, + .vfs_unmount = fullfs_unmount, + .vfs_root = layerfs_root, + .vfs_quotactl = layerfs_quotactl, + .vfs_statvfs = fullfs_statvfs, + .vfs_sync = layerfs_sync, + .vfs_loadvnode = layerfs_loadvnode, + .vfs_vget = layerfs_vget, + .vfs_fhtovp = layerfs_fhtovp, + .vfs_vptofh = layerfs_vptofh, + .vfs_init = layerfs_init, + .vfs_done = layerfs_done, + .vfs_snapshot = layerfs_snapshot, + .vfs_extattrctl = vfs_stdextattrctl, + .vfs_suspendctl = layerfs_suspendctl, + .vfs_renamelock_enter = layerfs_renamelock_enter, + .vfs_renamelock_exit = layerfs_renamelock_exit, + .vfs_fsync = (void *)eopnotsupp, + .vfs_opv_descs = fullfs_vnodeopv_descs +}; + +static int +full_modcmd(modcmd_t cmd, void *arg) +{ + int error; + + switch (cmd) { + case MODULE_CMD_INIT: + error = vfs_attach(&fullfs_vfsops); + if (error != 0) + break; + break; + case MODULE_CMD_FINI: + error = vfs_detach(&fullfs_vfsops); + if (error != 0) + break; + break; + default: + error = ENOTTY; + break; + } + return error; +} diff --git a/sys/miscfs/fullfs/full_vnops.c b/sys/miscfs/fullfs/full_vnops.c new file mode 100644 index 0000000000000..494f004cf8aa5 --- /dev/null +++ b/sys/miscfs/fullfs/full_vnops.c @@ -0,0 +1,143 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1999 National Aeronautics & Space Administration + * All rights reserved. + * + * This software was written by William Studenmund of the + * Numerical Aerospace Simulation Facility, NASA Ames Research Center. + * + * 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 National Aeronautics & Space Administration + * 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 NATIONAL AERONAUTICS & SPACE ADMINISTRATION + * ``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 ADMINISTRATION OR CONTRIB- + * UTORS 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. + */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * John Heidemann of the UCLA Ficus project. + * + * 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. + * + * @(#)null_vnops.c 8.6 (Berkeley) 5/27/95 + * + * Ancestors: + * @(#)lofs_vnops.c 1.2 (Berkeley) 6/18/92 + * Id: lofs_vnops.c,v 1.11 1992/05/30 10:05:43 jsp Exp jsp + * ...and... + * @(#)null_vnodeops.c 1.20 92/07/07 UCLA Ficus project + */ + +/* + * Full file-system. + * + * Implemented using layerfs, see layer_vnops.c for a description. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD$"); + +#include +#include +#include + +#include +#include +#include + +static int +fullfs_enospc(void *v __unused) +{ + return ENOSPC; +} + +/* + * Global VFS data structures. + */ + +int (**full_vnodeop_p)(void *); + +const struct vnodeopv_entry_desc full_vnodeop_entries[] = { + /* same as nullfs */ + { &vop_default_desc, layer_bypass }, + + { &vop_lookup_desc, layer_lookup }, + { &vop_setattr_desc, layer_setattr }, + { &vop_getattr_desc, layer_getattr }, + { &vop_access_desc, layer_access }, + { &vop_accessx_desc, genfs_accessx }, + { &vop_fsync_desc, layer_fsync }, + { &vop_inactive_desc, layer_inactive }, + { &vop_reclaim_desc, layer_reclaim }, + { &vop_print_desc, layer_print }, + { &vop_remove_desc, layer_remove }, + { &vop_rename_desc, layer_rename }, + { &vop_revoke_desc, layer_revoke }, + { &vop_rmdir_desc, layer_rmdir }, + + { &vop_open_desc, layer_open }, + { &vop_close_desc, layer_close }, + + { &vop_bmap_desc, layer_bmap }, + { &vop_getpages_desc, layer_getpages }, + { &vop_putpages_desc, layer_putpages }, + + /* fails with ENOSPC */ + { &vop_write_desc, fullfs_enospc }, + { &vop_create_desc, fullfs_enospc }, + { &vop_mkdir_desc, fullfs_enospc }, + { &vop_mknod_desc, fullfs_enospc }, + { &vop_symlink_desc, fullfs_enospc }, + { &vop_link_desc, fullfs_enospc }, + + { NULL, NULL } +}; + +const struct vnodeopv_desc full_vnodeop_opv_desc = { + &full_vnodeop_p, full_vnodeop_entries +}; diff --git a/sys/modules/Makefile b/sys/modules/Makefile index e6ae6c48c97e8..c746d5d2e8114 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -71,6 +71,7 @@ SUBDIR+= ffs SUBDIR+= filecore SUBDIR+= flash SUBDIR+= fss +SUBDIR+= full SUBDIR+= gpio SUBDIR+= gpioiic SUBDIR+= gpioow diff --git a/sys/modules/full/Makefile b/sys/modules/full/Makefile new file mode 100644 index 0000000000000..77abfcf9ab4fd --- /dev/null +++ b/sys/modules/full/Makefile @@ -0,0 +1,12 @@ +# $NetBSD$ + +.include "../Makefile.inc" + +.PATH: ${S}/miscfs/fullfs ${S}/miscfs/genfs + +KMOD= full +SRCS= full_vfsops.c full_vnops.c + +CWARNFLAGS.clang= -Wno-conversion + +.include diff --git a/sys/rump/fs/Makefile.rumpfscomp b/sys/rump/fs/Makefile.rumpfscomp index a455c533a10f1..9370420087461 100644 --- a/sys/rump/fs/Makefile.rumpfscomp +++ b/sys/rump/fs/Makefile.rumpfscomp @@ -3,8 +3,8 @@ .include -RUMPFSCOMP= cd9660 efs ext2fs fdesc ffs hfs kernfs lfs mfs msdos \ - nfs nilfs ntfs null ptyfs syspuffs sysvbfs \ +RUMPFSCOMP= cd9660 efs ext2fs fdesc ffs full hfs kernfs lfs mfs \ + msdos nfs nilfs ntfs null ptyfs syspuffs sysvbfs \ tmpfs udf umap union v7fs .if ${MKZFS} != "no" || make(rumpdescribe) diff --git a/sys/rump/fs/lib/libfull/Makefile b/sys/rump/fs/lib/libfull/Makefile new file mode 100644 index 0000000000000..b4971f224799e --- /dev/null +++ b/sys/rump/fs/lib/libfull/Makefile @@ -0,0 +1,13 @@ +# $NetBSD$ + +.PATH: ${.CURDIR}/../../../../miscfs/fullfs + +LIB= rumpfs_full +COMMENT=Disk-full simulation file system + +SRCS= full_vfsops.c full_vnops.c + +CWARNFLAGS.clang+= -Wno-conversion + +.include +.include diff --git a/sys/sys/mount.h b/sys/sys/mount.h index 1903fcd3c6979..caacc8cc882db 100644 --- a/sys/sys/mount.h +++ b/sys/sys/mount.h @@ -73,6 +73,7 @@ #define MOUNT_LFS "lfs" /* Log-based Filesystem */ #define MOUNT_FDESC "fdesc" /* File Descriptor Filesystem */ #define MOUNT_NULL "null" /* Minimal Filesystem Layer */ +#define MOUNT_FULL "full" /* Full Filesystem Layer */ #define MOUNT_OVERLAY "overlay" /* Minimal Overlay Filesystem Layer */ #define MOUNT_UMAP "umap" /* User/Group Identifier Remapping Filesystem */ #define MOUNT_KERNFS "kernfs" /* Kernel Information Filesystem */ diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h index ed789f1f9e6a9..01f08db2eb349 100644 --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -103,7 +103,7 @@ enum vtagtype { VT_AFS, VT_ISOFS, VT_UNION, VT_ADOSFS, VT_EXT2FS, VT_CODA, VT_FILECORE, VT_NTFS, VT_VFS, VT_OVERLAY, VT_SMBFS, VT_PTYFS, VT_TMPFS, VT_UDF, VT_SYSVBFS, VT_PUFFS, VT_HFS, VT_EFS, VT_ZFS, - VT_RUMP, VT_NILFS, VT_V7FS, VT_CHFS, VT_AUTOFS + VT_RUMP, VT_NILFS, VT_V7FS, VT_CHFS, VT_AUTOFS, VT_FULL }; #define VNODE_TAGS \ @@ -112,7 +112,7 @@ enum vtagtype { "VT_AFS", "VT_ISOFS", "VT_UNION", "VT_ADOSFS", "VT_EXT2FS", "VT_CODA", \ "VT_FILECORE", "VT_NTFS", "VT_VFS", "VT_OVERLAY", "VT_SMBFS", "VT_PTYFS", \ "VT_TMPFS", "VT_UDF", "VT_SYSVBFS", "VT_PUFFS", "VT_HFS", "VT_EFS", \ - "VT_ZFS", "VT_RUMP", "VT_NILFS", "VT_V7FS", "VT_CHFS", "VT_AUTOFS" + "VT_ZFS", "VT_RUMP", "VT_NILFS", "VT_V7FS", "VT_CHFS", "VT_AUTOFS", "VT_FULL" #if defined(_KERNEL) || defined(_KMEMUSER) struct vnode; diff --git a/tests/fs/Makefile b/tests/fs/Makefile index bb13b3496fc22..3cf0cc34d4549 100644 --- a/tests/fs/Makefile +++ b/tests/fs/Makefile @@ -13,6 +13,10 @@ TESTS_SUBDIRS+= vfs TESTS_SUBDIRS+= cd9660 .endif +.if ${MKFULLFS} != "no" +TESTS_SUBDIRS+= fullfs +.endif + .if ${MKHFS} != "no" TESTS_SUBDIRS+= hfs .endif diff --git a/tests/fs/fullfs/Makefile b/tests/fs/fullfs/Makefile new file mode 100644 index 0000000000000..91a76580ad32e --- /dev/null +++ b/tests/fs/fullfs/Makefile @@ -0,0 +1,13 @@ +# $NetBSD$ + +.include + +TESTSDIR= ${TESTSBASE}/fs/fullfs +WARNS= 4 + +TESTS_C= t_basic + +LDADD+= -lrumpfs_tmpfs -lrumpfs_full -lrumpvfs_layerfs # fs drivers +LDADD+= ${LIBRUMPBASE} # base + +.include diff --git a/tests/fs/fullfs/t_basic.c b/tests/fs/fullfs/t_basic.c new file mode 100644 index 0000000000000..7b1402175b78f --- /dev/null +++ b/tests/fs/fullfs/t_basic.c @@ -0,0 +1,203 @@ +/* $NetBSD$ */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "h_macros.h" + +#define MSTR "magic bus" + +static void +xput_tfile(const char *path, const char *mstr, size_t len) +{ + int fd; + + RL(fd = rump_sys_open(path, O_CREAT | O_RDWR, 0777)); + ATF_REQUIRE_EQ_MSG((ssize_t)len, rump_sys_write(fd, mstr, len), + "write to testfile: %s", strerror(errno)); + RL(rump_sys_close(fd)); +} + +static int +xread_tfile(const char *path, const char *mstr, size_t len) +{ + char buf[128]; + int fd; + + fd = rump_sys_open(path, O_RDONLY); + if (fd == -1) + return errno; + RL(rump_sys_read(fd, buf, sizeof(buf))); + RL(rump_sys_close(fd)); + if (strncmp(buf, mstr, len) == 0) + return 0; + return EPROGMISMATCH; +} + +static void +mountfull(const char *what, const char *mp, int flags) +{ + struct full_args fargs; + + memset(&fargs, 0, sizeof(fargs)); + fargs.fulla_target = __UNCONST(what); + RL(rump_sys_mount(MOUNT_FULL, mp, flags, &fargs, sizeof(fargs))); +} + +static void +mounttmpfs(const char *mp) +{ + struct tmpfs_args targs; + + memset(&targs, 0, sizeof(targs)); + targs.ta_version = TMPFS_ARGS_VERSION; + targs.ta_root_mode = 0777; + RL(rump_sys_mount(MOUNT_TMPFS, mp, 0, &targs, sizeof(targs))); +} + +static void +setup_fullfs(void) +{ + rump_init(); + RL(rump_sys_mkdir("/td1", 0777)); + RL(rump_sys_mkdir("/td2", 0777)); + mounttmpfs("/td1"); + mountfull("/td1", "/td2", 0); +} + + +ATF_TC(read_succeeds); +ATF_TC_HEAD(read_succeeds, tc) +{ + atf_tc_set_md_var(tc, "descr", "read through fullfs succeeds"); +} +ATF_TC_BODY(read_succeeds, tc) +{ + setup_fullfs(); + + xput_tfile("/td1/testfile", MSTR, sizeof(MSTR)); + + ATF_REQUIRE_EQ(0, xread_tfile("/td2/testfile", MSTR, sizeof(MSTR))); +} + + +ATF_TC(write_fails); +ATF_TC_HEAD(write_fails, tc) +{ + atf_tc_set_md_var(tc, "descr", "write through fullfs fails with ENOSPC"); +} +ATF_TC_BODY(write_fails, tc) +{ + int fd; + + setup_fullfs(); + + xput_tfile("/td1/testfile", MSTR, sizeof(MSTR)); + + RL(fd = rump_sys_open("/td2/testfile", O_WRONLY)); + ATF_REQUIRE_EQ(-1, rump_sys_write(fd, MSTR, sizeof(MSTR))); + ATF_REQUIRE_EQ(ENOSPC, errno); +} + + +ATF_TC(create_fails); +ATF_TC_HEAD(create_fails, tc) +{ + atf_tc_set_md_var(tc, "descr", "create through fullfs fails with ENOSPC"); +} +ATF_TC_BODY(create_fails, tc) +{ + setup_fullfs(); + + ATF_REQUIRE_EQ(-1, rump_sys_open("/td2/testfile", O_CREAT | O_WRONLY, 0600)); + ATF_REQUIRE_EQ(ENOSPC, errno); +} + + +ATF_TC(mkdir_fails); +ATF_TC_HEAD(mkdir_fails, tc) +{ + atf_tc_set_md_var(tc, "descr", "mkdir through fullfs fails with ENOSPC"); +} +ATF_TC_BODY(mkdir_fails, tc) +{ + setup_fullfs(); + + ATF_REQUIRE_EQ(-1, rump_sys_mkdir("/td2/testdir", 0777)); + ATF_REQUIRE_EQ(ENOSPC, errno); +} + + +ATF_TC(symlink_fails); +ATF_TC_HEAD(symlink_fails, tc) +{ + atf_tc_set_md_var(tc, "descr", "symlink through fullfs fails with ENOSPC"); +} +ATF_TC_BODY(symlink_fails, tc) +{ + setup_fullfs(); + + ATF_REQUIRE_EQ(-1, rump_sys_symlink("anything", "/td2/link")); + ATF_REQUIRE_EQ(ENOSPC, errno); +} + + +ATF_TC(remove_succeeds); +ATF_TC_HEAD(remove_succeeds, tc) +{ + atf_tc_set_md_var(tc, "descr", "remove through fullfs succeeds"); +} +ATF_TC_BODY(remove_succeeds, tc) +{ + setup_fullfs(); + + xput_tfile("/td1/testfile", MSTR, sizeof(MSTR)); + ATF_REQUIRE_EQ(0, rump_sys_unlink("/td2/testfile")); + + ATF_REQUIRE_EQ(ENOENT, xread_tfile("/td1/testfile", NULL, 0)); +} + + +ATF_TC(statvfs_shows_full); +ATF_TC_HEAD(statvfs_shows_full, tc) +{ + atf_tc_set_md_var(tc, "descr", "statvfs through fullfs shows full"); +} +ATF_TC_BODY(statvfs_shows_full, tc) +{ + struct statvfs sb; + + setup_fullfs(); + + RL(rump_sys_statvfs1("/td2", &sb, ST_WAIT)); + ATF_REQUIRE_EQ(0, sb.f_bfree); + ATF_REQUIRE_EQ(0, sb.f_bavail); +} + + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, read_succeeds); + ATF_TP_ADD_TC(tp, write_fails); + ATF_TP_ADD_TC(tp, create_fails); + ATF_TP_ADD_TC(tp, mkdir_fails); + ATF_TP_ADD_TC(tp, symlink_fails); + ATF_TP_ADD_TC(tp, remove_succeeds); + ATF_TP_ADD_TC(tp, statvfs_shows_full); + return atf_no_error(); +} From 8a0061c6134782e77348eeb0a805b170e79ccc86 Mon Sep 17 00:00:00 2001 From: "Hudson C. Dalpra" Date: Thu, 19 Mar 2026 19:52:46 +1300 Subject: [PATCH 2/2] fullfs: add runtime state control via ioctl --- distrib/sets/lists/base/mi | 1 + distrib/sets/lists/comp/mi | 1 + distrib/sets/lists/debug/mi | 1 + distrib/sets/lists/man/mi | 2 + distrib/sets/lists/manhtml/mi | 1 + sbin/Makefile | 1 + sbin/fullfsctl/Makefile | 10 + sbin/fullfsctl/fullfsctl.8 | 187 +++++++++++ sbin/fullfsctl/fullfsctl.c | 253 ++++++++++++++ sbin/mount_full/mount_full.8 | 36 +- sbin/mount_full/mount_full.c | 43 --- sys/miscfs/fullfs/Makefile | 2 +- sys/miscfs/fullfs/full.h | 77 +---- sys/miscfs/fullfs/full_ioctl.h | 35 ++ sys/miscfs/fullfs/full_vfsops.c | 106 ++---- sys/miscfs/fullfs/full_vnops.c | 263 ++++++++++----- tests/fs/fullfs/t_basic.c | 579 ++++++++++++++++++++++++++++++++ 17 files changed, 1293 insertions(+), 305 deletions(-) create mode 100644 sbin/fullfsctl/Makefile create mode 100644 sbin/fullfsctl/fullfsctl.8 create mode 100644 sbin/fullfsctl/fullfsctl.c create mode 100644 sys/miscfs/fullfs/full_ioctl.h diff --git a/distrib/sets/lists/base/mi b/distrib/sets/lists/base/mi index 968d2ff92965b..2ebff6848558a 100644 --- a/distrib/sets/lists/base/mi +++ b/distrib/sets/lists/base/mi @@ -393,6 +393,7 @@ ./sbin/fsck_v7fs base-sysutil-root v7fs ./sbin/fsdb base-sysutil-root ./sbin/fsirand base-sysutil-root +./sbin/fullfsctl base-miscfs-root fullfs ./sbin/gpt base-sysutil-root ./sbin/halt base-sysutil-root ./sbin/ifconfig base-netutil-root diff --git a/distrib/sets/lists/comp/mi b/distrib/sets/lists/comp/mi index 58b2d76d0b07d..1a076be627ad4 100644 --- a/distrib/sets/lists/comp/mi +++ b/distrib/sets/lists/comp/mi @@ -2788,6 +2788,7 @@ ./usr/include/miscfs/fdesc/fdesc.h comp-c-include ./usr/include/miscfs/fifofs/fifo.h comp-c-include ./usr/include/miscfs/fullfs/full.h comp-c-include +./usr/include/miscfs/fullfs/full_ioctl.h comp-c-include ./usr/include/miscfs/genfs/genfs.h comp-c-include ./usr/include/miscfs/genfs/genfs_node.h comp-c-include ./usr/include/miscfs/genfs/layer.h comp-c-include diff --git a/distrib/sets/lists/debug/mi b/distrib/sets/lists/debug/mi index 69b49c5df5c23..cc89213691fe3 100644 --- a/distrib/sets/lists/debug/mi +++ b/distrib/sets/lists/debug/mi @@ -368,6 +368,7 @@ ./usr/libdata/debug/sbin/fsck_v7fs.debug comp-sysutil-debug v7fs,debug ./usr/libdata/debug/sbin/fsdb.debug comp-sysutil-debug debug ./usr/libdata/debug/sbin/fsirand.debug comp-sysutil-debug debug +./usr/libdata/debug/sbin/fullfsctl.debug comp-miscfs-debug fullfs,debug ./usr/libdata/debug/sbin/gpt.debug comp-sysutil-debug debug ./usr/libdata/debug/sbin/ifconfig.debug comp-netutil-debug debug ./usr/libdata/debug/sbin/init.debug comp-sysutil-debug debug diff --git a/distrib/sets/lists/man/mi b/distrib/sets/lists/man/mi index 70e39302435fe..800a0e5cd31ba 100644 --- a/distrib/sets/lists/man/mi +++ b/distrib/sets/lists/man/mi @@ -2891,6 +2891,7 @@ ./usr/share/man/cat8/ftp-proxy.0 man-pf-catman pf,.cat ./usr/share/man/cat8/ftpd.0 man-netutil-catman .cat ./usr/share/man/cat8/fusermount.0 man-refuse-catman .cat +./usr/share/man/cat8/fullfsctl.0 man-miscfs-catman fullfs,.cat ./usr/share/man/cat8/fwctl.0 man-sysutil-catman .cat ./usr/share/man/cat8/genassym.sh.0 man-obsolete obsolete ./usr/share/man/cat8/getNAME.0 man-man-catman !makemandb,.cat @@ -6483,6 +6484,7 @@ ./usr/share/man/man8/fstyp.8 man-sysutil-man .man ./usr/share/man/man8/ftp-proxy.8 man-pf-man pf,.man ./usr/share/man/man8/ftpd.8 man-netutil-man .man +./usr/share/man/man8/fullfsctl.8 man-miscfs-man fullfs,.man ./usr/share/man/man8/fusermount.8 man-refuse-man .man ./usr/share/man/man8/fwctl.8 man-sysutil-man .man ./usr/share/man/man8/genassym.sh.8 man-obsolete obsolete diff --git a/distrib/sets/lists/manhtml/mi b/distrib/sets/lists/manhtml/mi index 4c30d403c0c69..da3a1ae224e8b 100644 --- a/distrib/sets/lists/manhtml/mi +++ b/distrib/sets/lists/manhtml/mi @@ -2612,6 +2612,7 @@ ./usr/share/man/html8/ftp-proxy.html man-pf-htmlman pf,html ./usr/share/man/html8/ftpd.html man-netutil-htmlman html ./usr/share/man/html8/fusermount.html man-refuse-htmlman html +./usr/share/man/html8/fullfsctl.html man-miscfs-htmlman fullfs,html ./usr/share/man/html8/fwctl.html man-sysutil-htmlman html ./usr/share/man/html8/getNAME.html man-man-htmlman !makemandb,html ./usr/share/man/html8/getencstat.html man-sysutil-htmlman html diff --git a/sbin/Makefile b/sbin/Makefile index 8ad2bbb96ee27..ad35e787448a9 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -93,6 +93,7 @@ SUBDIR+= mount_null .endif .if (${MKFULLFS} != "no") +SUBDIR+= fullfsctl SUBDIR+= mount_full .endif diff --git a/sbin/fullfsctl/Makefile b/sbin/fullfsctl/Makefile new file mode 100644 index 0000000000000..50a5d3a3b73f2 --- /dev/null +++ b/sbin/fullfsctl/Makefile @@ -0,0 +1,10 @@ +# $NetBSD$ + +.include + +PROG= fullfsctl +MAN= fullfsctl.8 + +CPPFLAGS+= -I${NETBSDSRCDIR}/sys + +.include diff --git a/sbin/fullfsctl/fullfsctl.8 b/sbin/fullfsctl/fullfsctl.8 new file mode 100644 index 0000000000000..3bf907b35c17d --- /dev/null +++ b/sbin/fullfsctl/fullfsctl.8 @@ -0,0 +1,187 @@ +.\" $NetBSD$ +.\" +.\" +.Dd March 6, 2026 +.Dt FULLFSCTL 8 +.Os +.Sh NAME +.Nm fullfsctl +.Nd control fullfs runtime fail state +.Sh SYNOPSIS +.Nm +.Fl m Ar mode +.Op Fl e Ar errno +.Op Fl o Ar ops +.Op Fl d Ar doom +.Op Fl r Ar rate +.Ar path +.Nm +.Fl s +.Ar path +.Sh DESCRIPTION +The +.Nm +utility controls the runtime failure behavior of a mounted +.Em fullfs +filesystem. +By default, a freshly mounted +.Em fullfs +fails all space-allocating operations with +.Er ENOSPC . +.Nm +allows the fail mode, error code, operation mask, and countdown +parameters to be changed at any time without remounting. +.Pp +.Ar path +may be the mountpoint or any file within the fullfs mount. +.Pp +The +.Fl m +flag is required for all set operations. +Flags omitted on the set path take their default values. +.Pp +The second synopsis form, +.Fl s , +queries and displays the current state in the form: +.Pp +.Dl mode= error= opmask=0x doom= rate= +.Pp +It cannot be combined with other flags. +.Sh OPTIONS +.Bl -tag -width "-e errno" +.It Fl m Ar mode +Set the fail mode. +One of: +.Bl -tag -width "random" -compact +.It Cm pass +All operations succeed; transparent passthrough. +.It Cm fail +All masked operations fail with the configured error. +.It Cm count +Allow +.Ar doom +masked operations, then fail subsequent ones. +Operations not in the mask pass through without decrementing the counter. +.It Cm bytes +Allow +.Ar doom +bytes of writes, then fail. +Non-write operations always pass through. +If a write exceeds the remaining budget, it is rejected entirely; +no partial writes occur. +.It Cm random +Each masked operation independently fails with the probability +set by +.Fl r . +Unmasked operations pass through without rolling. +.El +.It Fl e Ar errno +Error code to return on failure. +Accepts a symbolic name +.Po +.Li enospc , +.Li eio , +.Li edquot , +.Li efbig , +.Li eperm , +.Li eacces , +.Li erofs +.Pc +or a positive integer. +Default: +.Er ENOSPC . +.It Fl o Ar ops +Comma-separated list of operations to mask. +Valid names: +.Li write , +.Li create , +.Li mkdir , +.Li mknod , +.Li symlink , +.Li link , +.Li all , +.Li none . +Default: +.Li all . +.It Fl d Ar doom +Doom counter. +In +.Cm count +mode, the number of masked operations to allow before failing. +In +.Cm bytes +mode, the byte budget for writes. +Must be positive when the mode is +.Cm count +or +.Cm bytes . +Default: 0. +.It Fl r Ar rate +Failure rate percentage for +.Cm random +mode, from 1 to 99. +Boundary values 0 and 100 are rejected; use +.Cm pass +or +.Cm fail +instead. +Default: 0. +.It Fl s +Show the current state of the fullfs mount and exit. +Cannot be combined with +.Fl m +or other flags. +.El +.Sh EXAMPLES +Fail all operations with +.Er ENOSPC : +.Pp +.Dl fullfsctl -m fail /mnt/full +.Pp +Fail all operations with +.Er EIO : +.Pp +.Dl fullfsctl -m fail -e eio /mnt/full +.Pp +Fail only writes, allowing file creation: +.Pp +.Dl fullfsctl -m fail -o write /mnt/full +.Pp +Allow 5 operations, then fail: +.Pp +.Dl fullfsctl -m count -d 5 /mnt/full +.Pp +Allow 1024 bytes of writes, then fail: +.Pp +.Dl fullfsctl -m bytes -d 1024 /mnt/full +.Pp +Fail 50% of masked operations at random: +.Pp +.Dl fullfsctl -m random -r 50 /mnt/full +.Pp +Pass all operations through (disable failure injection): +.Pp +.Dl fullfsctl -m pass /mnt/full +.Pp +Show the current state: +.Pp +.Dl fullfsctl -s /mnt/full +.Sh CAVEATS +Writes performed via +.Xr mmap 2 +and +.Xr msync 2 +bypass +.Em fullfs +and are not subject to failure injection. +This is a layerfs architectural constraint; +.Fn putpages +operates at the VM page level, not the VOP level. +.Sh SEE ALSO +.Xr mount 8 , +.Xr mount_full 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Nx 11 . diff --git a/sbin/fullfsctl/fullfsctl.c b/sbin/fullfsctl/fullfsctl.c new file mode 100644 index 0000000000000..7eabbf42b68c9 --- /dev/null +++ b/sbin/fullfsctl/fullfsctl.c @@ -0,0 +1,253 @@ +/* $NetBSD$ */ + +#include +__RCSID("$NetBSD$"); + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +static void usage(void) __dead; + +static const struct { + const char *name; + int value; +} modes[] = { + { "pass", FULLFS_MODE_PASS }, + { "fail", FULLFS_MODE_FAIL }, + { "count", FULLFS_MODE_COUNT }, + { "bytes", FULLFS_MODE_BYTES }, + { "random", FULLFS_MODE_RANDOM }, +}; + +static const struct { + const char *name; + int value; +} errnos[] = { + { "enospc", ENOSPC }, + { "eio", EIO }, + { "edquot", EDQUOT }, + { "efbig", EFBIG }, + { "eperm", EPERM }, + { "eacces", EACCES }, + { "erofs", EROFS }, +}; + +static const struct { + const char *name; + int value; +} ops[] = { + { "write", FULLFS_OP_WRITE }, + { "create", FULLFS_OP_CREATE }, + { "mkdir", FULLFS_OP_MKDIR }, + { "mknod", FULLFS_OP_MKNOD }, + { "symlink", FULLFS_OP_SYMLINK }, + { "link", FULLFS_OP_LINK }, + { "all", FULLFS_OP_ALL }, + { "none", FULLFS_OP_NONE }, +}; + +static int +parse_mode(const char *s) +{ + size_t i; + + for (i = 0; i < __arraycount(modes); i++) + if (strcasecmp(s, modes[i].name) == 0) + return modes[i].value; + + errx(1, "unknown mode: %s", s); +} + +static const char * +mode_name(int mode) +{ + size_t i; + + for (i = 0; i < __arraycount(modes); i++) + if (modes[i].value == mode) + return modes[i].name; + + return "unknown"; +} + +static int +parse_error(const char *s) +{ + size_t i; + char *ep; + long val; + + for (i = 0; i < __arraycount(errnos); i++) + if (strcasecmp(s, errnos[i].name) == 0) + return errnos[i].value; + + errno = 0; + val = strtol(s, &ep, 10); + if (*s == '\0' || *ep != '\0' || errno != 0 || val <= 0) + errx(1, "invalid error: %s", s); + return (int)val; +} + +static int +parse_opmask(const char *s) +{ + int mask, found; + char *copy, *token, *last; + size_t i; + + copy = strdup(s); + if (!copy) + err(1, "strdup"); + + mask = 0; + for (token = strtok_r(copy, ",", &last); token != NULL; + token = strtok_r(NULL, ",", &last)) { + found = 0; + for (i = 0; i < __arraycount(ops); i++) { + if (strcasecmp(token, ops[i].name) == 0) { + mask |= ops[i].value; + found = 1; + break; + } + } + if (!found) + errx(1, "unknown op: %s", token); + } + + free(copy); + return mask; +} + +static int64_t +parse_doom(const char *s) +{ + char *ep; + long long val; + + errno = 0; + val = strtoll(s, &ep, 10); + if (*s == '\0' || *ep != '\0' || errno != 0) + errx(1, "invalid doom value: %s", s); + return (int64_t)val; +} + +static int +parse_rate(const char *s) +{ + char *ep; + long val; + + errno = 0; + val = strtol(s, &ep, 10); + if (*s == '\0' || *ep != '\0' || errno != 0) + errx(1, "invalid rate value: %s", s); + return (int)val; +} + +static void +show_state(int fd) +{ + struct fullfs_ctl fc; + + if (ioctl(fd, FULLFS_GETSTATE, &fc) == -1) + err(1, "FULLFS_GETSTATE"); + + printf("mode=%s error=%d opmask=0x%x doom=%lld rate=%d\n", + mode_name(fc.fc_mode), fc.fc_error, fc.fc_opmask, + (long long)fc.fc_doom, fc.fc_rate); +} + +int +main(int argc, char **argv) +{ + int ch, fd, mflag, sflag; + const char *marg, *earg, *oarg, *darg, *rarg; + struct fullfs_ctl fc; + + mflag = sflag = 0; + marg = earg = oarg = darg = rarg = NULL; + setprogname(argv[0]); + + while ((ch = getopt(argc, argv, "d:e:m:o:r:s")) != -1) { + switch (ch) { + case 'd': + darg = optarg; + break; + case 'e': + earg = optarg; + break; + case 'm': + mflag = 1; + marg = optarg; + break; + case 'o': + oarg = optarg; + break; + case 'r': + rarg = optarg; + break; + case 's': + sflag = 1; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + + if (mflag && sflag) + usage(); + + if (!mflag && !sflag) + usage(); + + if (!mflag && (earg || oarg || darg || rarg)) + usage(); + + fd = open(argv[0], O_RDONLY); + if (fd == -1) + err(1, "%s", argv[0]); + + if (sflag) { + show_state(fd); + close(fd); + return 0; + } + + memset(&fc, 0, sizeof(fc)); + fc.fc_mode = parse_mode(marg); + fc.fc_error = earg ? parse_error(earg) : ENOSPC; + fc.fc_opmask = oarg ? parse_opmask(oarg) : FULLFS_OP_ALL; + fc.fc_doom = darg ? parse_doom(darg) : 0; + fc.fc_rate = rarg ? parse_rate(rarg) : 0; + + if (ioctl(fd, FULLFS_SETSTATE, &fc) == -1) + err(1, "FULLFS_SETSTATE"); + + close(fd); + return 0; +} + +static void +usage(void) +{ + fprintf(stderr, + "usage: %s [-m mode] [-e errno] [-o ops] [-d doom] [-r rate]" + " mountpoint \n" + " %s -s mountpoint\n", + getprogname(), getprogname()); + exit(1); +} diff --git a/sbin/mount_full/mount_full.8 b/sbin/mount_full/mount_full.8 index 81f29b86b2305..f40f8b0c2cf75 100644 --- a/sbin/mount_full/mount_full.8 +++ b/sbin/mount_full/mount_full.8 @@ -1,34 +1,5 @@ .\" $NetBSD$ .\" -.\" Copyright (c) 1992, 1993, 1994 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" This code is derived from software donated to Berkeley by -.\" Jan-Simon Pendry. -.\" -.\" 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. .\" .Dd March 6, 2026 .Dt MOUNT_FULL 8 @@ -63,6 +34,10 @@ immediately, without touching the underlying filesystem. .Pp This makes it possible to test an application's handling of disk-full conditions without filling an actual disk. +The failure behavior is runtime-configurable via +.Xr fullfsctl 8 , +which can change the fail mode, error code, and operation mask +without remounting. .Pp Both .Ar target @@ -70,7 +45,7 @@ and .Ar mount-point are converted to absolute paths before use. .Sh BEHAVIOR -The following operations always fail with +The following operations fail by default with .Er ENOSPC through a fullfs mount: .Bl -bullet -compact @@ -142,6 +117,7 @@ echo $? # non-zero; "No space left on device" df /mnt/full .Ed .Sh SEE ALSO +.Xr fullfsctl 8 , .Xr mount 8 , .Xr mount_null 8 .Sh HISTORY diff --git a/sbin/mount_full/mount_full.c b/sbin/mount_full/mount_full.c index a3a851a28c2e8..ecfbc95051d38 100644 --- a/sbin/mount_full/mount_full.c +++ b/sbin/mount_full/mount_full.c @@ -1,50 +1,7 @@ /* $NetBSD$ */ -/* - * Copyright (c) 1992, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software donated to Berkeley by - * Jan-Simon Pendry. - * - * 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 -#ifndef lint -__COPYRIGHT("@(#) Copyright (c) 1992, 1993, 1994\ - The Regents of the University of California. All rights reserved."); -#endif /* not lint */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)mount_full.c 8.6 (Berkeley) 4/26/95"; -#else __RCSID("$NetBSD$"); -#endif -#endif /* not lint */ #include #include diff --git a/sys/miscfs/fullfs/Makefile b/sys/miscfs/fullfs/Makefile index f2f65ec0b3e2b..dccad62254b64 100644 --- a/sys/miscfs/fullfs/Makefile +++ b/sys/miscfs/fullfs/Makefile @@ -2,6 +2,6 @@ INCSDIR= /usr/include/miscfs/fullfs -INCS= full.h +INCS= full.h full_ioctl.h .include diff --git a/sys/miscfs/fullfs/full.h b/sys/miscfs/fullfs/full.h index 99a7019cf2407..52ca0a8a3d4b5 100644 --- a/sys/miscfs/fullfs/full.h +++ b/sys/miscfs/fullfs/full.h @@ -1,73 +1,5 @@ /* $NetBSD$ */ -/* - * Copyright (c) 1999 National Aeronautics & Space Administration - * All rights reserved. - * - * This software was written by William Studenmund of the - * Numerical Aerospace Simulation Facility, NASA Ames Research Center. - * - * 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 National Aeronautics & Space Administration - * 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 NATIONAL AERONAUTICS & SPACE ADMINISTRATION - * ``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 ADMINISTRATION OR CONTRIB- - * UTORS 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. - */ - -/* - * Copyright (c) 1992, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software donated to Berkeley by - * Jan-Simon Pendry. - * - * 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. - * - * from: Id: lofs.h,v 1.8 1992/05/30 10:05:43 jsp Exp - * @(#)null.h 8.2 (Berkeley) 1/21/94 - */ - #ifndef _MISCFS_FULLFS_H_ #define _MISCFS_FULLFS_H_ @@ -80,8 +12,17 @@ struct full_args { #define fulla_export la.export #ifdef _KERNEL +#include + struct full_mount { struct layer_mount lm; /* generic layerfs mount stuff */ + + kmutex_t fullm_lock; + int fullm_mode; + int fullm_opmask; + int fullm_error; + int fullm_rate; + int64_t fullm_doom; }; #define fullm_rootvp lm.layerm_rootvp #define fullm_export lm.layerm_export diff --git a/sys/miscfs/fullfs/full_ioctl.h b/sys/miscfs/fullfs/full_ioctl.h new file mode 100644 index 0000000000000..97bb347215199 --- /dev/null +++ b/sys/miscfs/fullfs/full_ioctl.h @@ -0,0 +1,35 @@ +/* $NetBSD$ */ + +#ifndef _MISCFS_FULLFS_IOCTL_H_ +#define _MISCFS_FULLFS_IOCTL_H_ + +#include +#include + +#define FULLFS_MODE_PASS 0 +#define FULLFS_MODE_FAIL 1 +#define FULLFS_MODE_COUNT 2 +#define FULLFS_MODE_BYTES 3 +#define FULLFS_MODE_RANDOM 4 + +#define FULLFS_OP_NONE 0x00 +#define FULLFS_OP_WRITE 0x01 +#define FULLFS_OP_CREATE 0x02 +#define FULLFS_OP_MKDIR 0x04 +#define FULLFS_OP_MKNOD 0x08 +#define FULLFS_OP_SYMLINK 0x10 +#define FULLFS_OP_LINK 0x20 +#define FULLFS_OP_ALL 0x3F + +struct fullfs_ctl { + int fc_mode; + int fc_opmask; + int fc_error; + int fc_rate; + int64_t fc_doom; +}; + +#define FULLFS_GETSTATE _IOR('F', 30, struct fullfs_ctl) +#define FULLFS_SETSTATE _IOW('F', 31, struct fullfs_ctl) + +#endif /* _MISCFS_FULLFS_IOCTL_H_ */ diff --git a/sys/miscfs/fullfs/full_vfsops.c b/sys/miscfs/fullfs/full_vfsops.c index 153565ff7bda1..a08d7e8bcc54e 100644 --- a/sys/miscfs/fullfs/full_vfsops.c +++ b/sys/miscfs/fullfs/full_vfsops.c @@ -1,74 +1,5 @@ /* $NetBSD$ */ -/* - * Copyright (c) 1999 National Aeronautics & Space Administration - * All rights reserved. - * - * This software was written by William Studenmund of the - * Numerical Aerospace Simulation Facility, NASA Ames Research Center. - * - * 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 National Aeronautics & Space Administration - * 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 NATIONAL AERONAUTICS & SPACE ADMINISTRATION - * ``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 ADMINISTRATION OR CONTRIB- - * UTORS 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. - */ - -/* - * Copyright (c) 1992, 1993, 1995 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software donated to Berkeley by - * Jan-Simon Pendry. - * - * 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. - * - * from: Id: lofs_vfsops.c,v 1.9 1992/05/30 10:26:24 jsp Exp - * from: @(#)lofs_vfsops.c 1.2 (Berkeley) 6/18/92 - * @(#)null_vfsops.c 8.7 (Berkeley) 5/14/95 - */ - /* * Full file-system: VFS operations. * @@ -86,6 +17,7 @@ __KERNEL_RCSID(0, "$NetBSD$"); #include #include +#include #include MODULE(MODULE_CLASS_VFS, full, "layerfs"); @@ -152,6 +84,13 @@ fullfs_mount(struct mount *mp, const char *path, void *data, size_t *data_len) return error; } + mutex_init(&nmp->fullm_lock, MUTEX_DEFAULT, IPL_NONE); + nmp->fullm_mode = FULLFS_MODE_FAIL; + nmp->fullm_error = ENOSPC; + nmp->fullm_opmask = FULLFS_OP_ALL; + nmp->fullm_rate = 0; + nmp->fullm_doom = 0; + nmp->fullm_size = sizeof(struct full_node); nmp->fullm_tag = VT_FULL; nmp->fullm_bypass = layer_bypass; @@ -162,6 +101,7 @@ fullfs_mount(struct mount *mp, const char *path, void *data, size_t *data_len) error = layer_node_create(mp, lowerrootvp, &vp); if (error) { vrele(lowerrootvp); + mutex_destroy(&nmp->fullm_lock); kmem_free(nmp, sizeof(struct full_mount)); return error; } @@ -176,8 +116,12 @@ fullfs_mount(struct mount *mp, const char *path, void *data, size_t *data_len) error = set_statvfs_info(path, UIO_USERSPACE, args->la.target, UIO_USERSPACE, mp->mnt_op->vfs_name, mp, curlwp); - if (error) + if (error) { + vgone(nmp->fullm_rootvp); + mutex_destroy(&nmp->fullm_lock); + kmem_free(nmp, sizeof(struct full_mount)); return error; + } if (mp->mnt_lower->mnt_flag & MNT_LOCAL) mp->mnt_flag |= MNT_LOCAL; @@ -204,6 +148,7 @@ fullfs_unmount(struct mount *mp, int mntflags) vgone(full_rootvp); /* Finally, destroy the mount point structures. */ + mutex_destroy(&nmp->fullm_lock); kmem_free(mp->mnt_data, sizeof(struct full_mount)); mp->mnt_data = NULL; return 0; @@ -212,6 +157,7 @@ fullfs_unmount(struct mount *mp, int mntflags) int fullfs_statvfs(struct mount *mp, struct statvfs *sbp) { + struct full_mount *nmp = MOUNTTOFULLMOUNT(mp); int error; error = layerfs_statvfs(mp, sbp); @@ -219,13 +165,17 @@ fullfs_statvfs(struct mount *mp, struct statvfs *sbp) return error; /* A full filesystem has no free space. */ - sbp->f_bfree = 0; - sbp->f_bavail = 0; - sbp->f_bresvd = 0; - sbp->f_ffree = 0; - sbp->f_favail = 0; - sbp->f_fresvd = 0; + mutex_enter(&nmp->fullm_lock); + if (nmp->fullm_mode != FULLFS_MODE_PASS) { + sbp->f_bfree = 0; + sbp->f_bavail = 0; + sbp->f_bresvd = 0; + sbp->f_ffree = 0; + sbp->f_favail = 0; + sbp->f_fresvd = 0; + } + mutex_exit(&nmp->fullm_lock); return 0; } @@ -269,13 +219,9 @@ full_modcmd(modcmd_t cmd, void *arg) switch (cmd) { case MODULE_CMD_INIT: error = vfs_attach(&fullfs_vfsops); - if (error != 0) - break; break; case MODULE_CMD_FINI: error = vfs_detach(&fullfs_vfsops); - if (error != 0) - break; break; default: error = ENOTTY; diff --git a/sys/miscfs/fullfs/full_vnops.c b/sys/miscfs/fullfs/full_vnops.c index 494f004cf8aa5..cf9c6eae3fa98 100644 --- a/sys/miscfs/fullfs/full_vnops.c +++ b/sys/miscfs/fullfs/full_vnops.c @@ -1,78 +1,5 @@ /* $NetBSD$ */ -/* - * Copyright (c) 1999 National Aeronautics & Space Administration - * All rights reserved. - * - * This software was written by William Studenmund of the - * Numerical Aerospace Simulation Facility, NASA Ames Research Center. - * - * 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 National Aeronautics & Space Administration - * 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 NATIONAL AERONAUTICS & SPACE ADMINISTRATION - * ``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 ADMINISTRATION OR CONTRIB- - * UTORS 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. - */ - -/* - * Copyright (c) 1992, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * John Heidemann of the UCLA Ficus project. - * - * 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. - * - * @(#)null_vnops.c 8.6 (Berkeley) 5/27/95 - * - * Ancestors: - * @(#)lofs_vnops.c 1.2 (Berkeley) 6/18/92 - * Id: lofs_vnops.c,v 1.11 1992/05/30 10:05:43 jsp Exp jsp - * ...and... - * @(#)null_vnodeops.c 1.20 92/07/07 UCLA Ficus project - */ - /* * Full file-system. * @@ -85,15 +12,186 @@ __KERNEL_RCSID(0, "$NetBSD$"); #include #include #include +#include +#include #include #include #include +#include + +static int +fullfs_should_fail(struct mount *mp, int opmask_bit, int64_t nbytes) +{ + struct full_mount *nmp = MOUNTTOFULLMOUNT(mp); + int error; + + mutex_enter(&nmp->fullm_lock); + if ((nmp->fullm_opmask & opmask_bit) == 0) { + mutex_exit(&nmp->fullm_lock); + return 0; + } + + switch (nmp->fullm_mode) { + case FULLFS_MODE_PASS: + error = 0; + break; + case FULLFS_MODE_FAIL: + error = nmp->fullm_error; + break; + case FULLFS_MODE_COUNT: + if (nmp->fullm_doom <= 0) + error = nmp->fullm_error; + else { + nmp->fullm_doom -= 1; + error = 0; + } + break; + case FULLFS_MODE_BYTES: + if (nbytes == 0) { + error = 0; + break; + } + if (nbytes > nmp->fullm_doom) + error = nmp->fullm_error; + else { + nmp->fullm_doom -= nbytes; + error = 0; + } + break; + case FULLFS_MODE_RANDOM: + if ((int)(cprng_fast32() % 100) < nmp->fullm_rate) + error = nmp->fullm_error; + else + error = 0; + break; + default: + KASSERTMSG(0, "%s: invalid fullm_mode %d", __func__, nmp->fullm_mode); + error = nmp->fullm_error; + break; + } + + mutex_exit(&nmp->fullm_lock); + return error; +} + +static int +fullfs_write(void *v) +{ + struct vop_write_args *ap = v; + int error; + + error = fullfs_should_fail(ap->a_vp->v_mount, FULLFS_OP_WRITE, + ap->a_uio->uio_resid); + if (error) + return error; + return layer_bypass(v); +} + +static int +fullfs_create(void *v) +{ + struct vop_create_v3_args *ap = v; + int error; + + error = fullfs_should_fail(ap->a_dvp->v_mount, FULLFS_OP_CREATE, 0); + if (error) + return error; + return layer_bypass(v); +} + +static int +fullfs_mkdir(void *v) +{ + struct vop_mkdir_v3_args *ap = v; + int error; + + error = fullfs_should_fail(ap->a_dvp->v_mount, FULLFS_OP_MKDIR, 0); + if (error) + return error; + return layer_bypass(v); +} + +static int +fullfs_mknod(void *v) +{ + struct vop_mknod_v3_args *ap = v; + int error; + + error = fullfs_should_fail(ap->a_dvp->v_mount, FULLFS_OP_MKNOD, 0); + if (error) + return error; + return layer_bypass(v); +} + +static int +fullfs_symlink(void *v) +{ + struct vop_symlink_v3_args *ap = v; + int error; + + error = fullfs_should_fail(ap->a_dvp->v_mount, FULLFS_OP_SYMLINK, 0); + if (error) + return error; + return layer_bypass(v); +} + +static int +fullfs_link(void *v) +{ + struct vop_link_v2_args *ap = v; + int error; + + error = fullfs_should_fail(ap->a_dvp->v_mount, FULLFS_OP_LINK, 0); + if (error) + return error; + return layer_bypass(v); +} static int -fullfs_enospc(void *v __unused) +fullfs_ioctl(void *v) { - return ENOSPC; + struct vop_ioctl_args *ap = v; + struct full_mount *nmp = MOUNTTOFULLMOUNT(ap->a_vp->v_mount); + struct fullfs_ctl *fc = (struct fullfs_ctl *)ap->a_data; + + switch (ap->a_command) { + case FULLFS_GETSTATE: + mutex_enter(&nmp->fullm_lock); + fc->fc_mode = nmp->fullm_mode; + fc->fc_opmask = nmp->fullm_opmask; + fc->fc_error = nmp->fullm_error; + fc->fc_rate = nmp->fullm_rate; + fc->fc_doom = nmp->fullm_doom; + mutex_exit(&nmp->fullm_lock); + return 0; + case FULLFS_SETSTATE: + if (fc->fc_mode < FULLFS_MODE_PASS || + fc->fc_mode > FULLFS_MODE_RANDOM) + return EINVAL; + if (fc->fc_error <= 0) + return EINVAL; + if (fc->fc_opmask & ~FULLFS_OP_ALL) + return EINVAL; + if (fc->fc_mode == FULLFS_MODE_COUNT && fc->fc_doom <= 0) + return EINVAL; + if (fc->fc_mode == FULLFS_MODE_BYTES && fc->fc_doom <= 0) + return EINVAL; + if (fc->fc_mode == FULLFS_MODE_RANDOM && + (fc->fc_rate < 1 || fc->fc_rate > 99)) + return EINVAL; + + mutex_enter(&nmp->fullm_lock); + nmp->fullm_mode = fc->fc_mode; + nmp->fullm_opmask = fc->fc_opmask; + nmp->fullm_error = fc->fc_error; + nmp->fullm_rate = fc->fc_rate; + nmp->fullm_doom = fc->fc_doom; + mutex_exit(&nmp->fullm_lock); + return 0; + default: + return layer_bypass(v); + } } /* @@ -103,7 +201,6 @@ fullfs_enospc(void *v __unused) int (**full_vnodeop_p)(void *); const struct vnodeopv_entry_desc full_vnodeop_entries[] = { - /* same as nullfs */ { &vop_default_desc, layer_bypass }, { &vop_lookup_desc, layer_lookup }, @@ -127,13 +224,13 @@ const struct vnodeopv_entry_desc full_vnodeop_entries[] = { { &vop_getpages_desc, layer_getpages }, { &vop_putpages_desc, layer_putpages }, - /* fails with ENOSPC */ - { &vop_write_desc, fullfs_enospc }, - { &vop_create_desc, fullfs_enospc }, - { &vop_mkdir_desc, fullfs_enospc }, - { &vop_mknod_desc, fullfs_enospc }, - { &vop_symlink_desc, fullfs_enospc }, - { &vop_link_desc, fullfs_enospc }, + { &vop_write_desc, fullfs_write }, + { &vop_create_desc, fullfs_create }, + { &vop_mkdir_desc, fullfs_mkdir }, + { &vop_mknod_desc, fullfs_mknod }, + { &vop_symlink_desc, fullfs_symlink }, + { &vop_link_desc, fullfs_link }, + { &vop_ioctl_desc, fullfs_ioctl }, { NULL, NULL } }; diff --git a/tests/fs/fullfs/t_basic.c b/tests/fs/fullfs/t_basic.c index 7b1402175b78f..e1eb437cfd127 100644 --- a/tests/fs/fullfs/t_basic.c +++ b/tests/fs/fullfs/t_basic.c @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -16,6 +17,7 @@ #include #include +#include #include #include "h_macros.h" @@ -70,6 +72,34 @@ mounttmpfs(const char *mp) RL(rump_sys_mount(MOUNT_TMPFS, mp, 0, &targs, sizeof(targs))); } +static void +fullfs_setstate(int mode, int opmask, int error, int rate, int64_t doom) +{ + struct fullfs_ctl fc; + int fd; + + memset(&fc, 0, sizeof(fc)); + fc.fc_mode = mode; + fc.fc_opmask = opmask; + fc.fc_error = error; + fc.fc_rate = rate; + fc.fc_doom = doom; + + RL(fd = rump_sys_open("/td2", O_RDONLY)); + RL(rump_sys_ioctl(fd, FULLFS_SETSTATE, &fc)); + RL(rump_sys_close(fd)); +} + +static void +fullfs_getstate(struct fullfs_ctl *fc) +{ + int fd; + + RL(fd = rump_sys_open("/td2", O_RDONLY)); + RL(rump_sys_ioctl(fd, FULLFS_GETSTATE, fc)); + RL(rump_sys_close(fd)); +} + static void setup_fullfs(void) { @@ -190,6 +220,536 @@ ATF_TC_BODY(statvfs_shows_full, tc) } +ATF_TC(getstate_initial); +ATF_TC_HEAD(getstate_initial, tc) +{ + atf_tc_set_md_var(tc, "descr", "GETSTATE returns default mount state"); +} +ATF_TC_BODY(getstate_initial, tc) +{ + struct fullfs_ctl fc; + + setup_fullfs(); + fullfs_getstate(&fc); + + ATF_REQUIRE_EQ(FULLFS_MODE_FAIL, fc.fc_mode); + ATF_REQUIRE_EQ(FULLFS_OP_ALL, fc.fc_opmask); + ATF_REQUIRE_EQ(ENOSPC, fc.fc_error); + ATF_REQUIRE_EQ(0, fc.fc_rate); + ATF_REQUIRE_EQ(0, fc.fc_doom); +} + + +ATF_TC(toggle_pass_fail); +ATF_TC_HEAD(toggle_pass_fail, tc) +{ + atf_tc_set_md_var(tc, "descr", "toggling between PASS and FAIL modes"); +} +ATF_TC_BODY(toggle_pass_fail, tc) +{ + int fd; + + setup_fullfs(); + + xput_tfile("/td1/testfile", MSTR, sizeof(MSTR)); + + RL(fd = rump_sys_open("/td2/testfile", O_WRONLY)); + ATF_REQUIRE_EQ(-1, rump_sys_write(fd, MSTR, sizeof(MSTR))); + ATF_REQUIRE_EQ(ENOSPC, errno); + RL(rump_sys_close(fd)); + + fullfs_setstate(FULLFS_MODE_PASS, FULLFS_OP_ALL, ENOSPC, 0, 0); + RL(fd = rump_sys_open("/td2/testfile", O_WRONLY)); + ATF_REQUIRE_EQ((ssize_t)sizeof(MSTR), rump_sys_write(fd, MSTR, sizeof(MSTR))); + RL(rump_sys_close(fd)); + + fullfs_setstate(FULLFS_MODE_FAIL, FULLFS_OP_ALL, ENOSPC, 0, 0); + RL(fd = rump_sys_open("/td2/testfile", O_WRONLY)); + ATF_REQUIRE_EQ(-1, rump_sys_write(fd, MSTR, sizeof(MSTR))); + ATF_REQUIRE_EQ(ENOSPC, errno); + RL(rump_sys_close(fd)); +} + +ATF_TC(error_code); +ATF_TC_HEAD(error_code, tc) +{ + atf_tc_set_md_var(tc, "descr", "configurable error code is honored"); +} +ATF_TC_BODY(error_code, tc) +{ + int fd; + + setup_fullfs(); + xput_tfile("/td1/testfile", MSTR, sizeof(MSTR)); + + fullfs_setstate(FULLFS_MODE_FAIL, FULLFS_OP_ALL, EIO, 0, 0); + RL(fd = rump_sys_open("/td2/testfile", O_WRONLY)); + ATF_REQUIRE_EQ(-1, rump_sys_write(fd, MSTR, sizeof(MSTR))); + ATF_REQUIRE_EQ(EIO, errno); + RL(rump_sys_close(fd)); + + fullfs_setstate(FULLFS_MODE_FAIL, FULLFS_OP_ALL, EDQUOT, 0, 0); + RL(fd = rump_sys_open("/td2/testfile", O_WRONLY)); + ATF_REQUIRE_EQ(-1, rump_sys_write(fd, MSTR, sizeof(MSTR))); + ATF_REQUIRE_EQ(EDQUOT, errno); + RL(rump_sys_close(fd)); +} + + +ATF_TC(error_roundtrip); +ATF_TC_HEAD(error_roundtrip, tc) +{ + atf_tc_set_md_var(tc, "descr", "error code survives SETSTATE/GETSTATE"); +} +ATF_TC_BODY(error_roundtrip, tc) +{ + struct fullfs_ctl fc; + + setup_fullfs(); + + fullfs_setstate(FULLFS_MODE_FAIL, FULLFS_OP_ALL, EFBIG, 0, 0); + fullfs_getstate(&fc); + ATF_REQUIRE_EQ(EFBIG, fc.fc_error); +} + + +ATF_TC(opmask_write_only); +ATF_TC_HEAD(opmask_write_only, tc) +{ + atf_tc_set_md_var(tc, "descr", "opmask WRITE blocks writes but not creates"); +} +ATF_TC_BODY(opmask_write_only, tc) +{ + int fd; + + setup_fullfs(); + xput_tfile("/td1/testfile", MSTR, sizeof(MSTR)); + + fullfs_setstate(FULLFS_MODE_FAIL, FULLFS_OP_WRITE, ENOSPC, 0, 0); + + RL(fd = rump_sys_open("/td2/testfile", O_WRONLY)); + ATF_REQUIRE_EQ(-1, rump_sys_write(fd, MSTR, sizeof(MSTR))); + ATF_REQUIRE_EQ(ENOSPC, errno); + RL(rump_sys_close(fd)); + + RL(fd = rump_sys_open("/td2/newfile", O_CREAT | O_WRONLY, 0600)); + RL(rump_sys_close(fd)); +} + + +ATF_TC(opmask_create_only); +ATF_TC_HEAD(opmask_create_only, tc) +{ + atf_tc_set_md_var(tc, "descr", "opmask CREATE blocks creates but not writes"); +} +ATF_TC_BODY(opmask_create_only, tc) +{ + int fd; + + setup_fullfs(); + xput_tfile("/td1/testfile", MSTR, sizeof(MSTR)); + + fullfs_setstate(FULLFS_MODE_FAIL, FULLFS_OP_CREATE, ENOSPC, 0, 0); + + ATF_REQUIRE_EQ(-1, rump_sys_open("/td2/newfile", O_CREAT | O_WRONLY, 0600)); + ATF_REQUIRE_EQ(ENOSPC, errno); + + RL(fd = rump_sys_open("/td2/testfile", O_WRONLY)); + ATF_REQUIRE_EQ((ssize_t)sizeof(MSTR), rump_sys_write(fd, MSTR, sizeof(MSTR))); + RL(rump_sys_close(fd)); +} + + +ATF_TC(opmask_none); +ATF_TC_HEAD(opmask_none, tc) +{ + atf_tc_set_md_var(tc, "descr", "opmask NONE lets all operations through"); +} +ATF_TC_BODY(opmask_none, tc) +{ + int fd; + + setup_fullfs(); + + fullfs_setstate(FULLFS_MODE_FAIL, FULLFS_OP_NONE, ENOSPC, 0, 0); + + RL(fd = rump_sys_open("/td2/newfile", O_CREAT | O_WRONLY, 0600)); + ATF_REQUIRE_EQ((ssize_t)sizeof(MSTR), rump_sys_write(fd, MSTR, sizeof(MSTR))); + RL(rump_sys_close(fd)); +} + + +ATF_TC(opmask_multiple); +ATF_TC_HEAD(opmask_multiple, tc) +{ + atf_tc_set_md_var(tc, "descr", "opmask with multiple bits set"); +} +ATF_TC_BODY(opmask_multiple, tc) +{ + int fd; + + setup_fullfs(); + xput_tfile("/td1/testfile", MSTR, sizeof(MSTR)); + + fullfs_setstate(FULLFS_MODE_FAIL, FULLFS_OP_WRITE | FULLFS_OP_MKDIR, ENOSPC, 0, 0); + + RL(fd = rump_sys_open("/td2/testfile", O_WRONLY)); + ATF_REQUIRE_EQ(-1, rump_sys_write(fd, MSTR, sizeof(MSTR))); + ATF_REQUIRE_EQ(ENOSPC, errno); + RL(rump_sys_close(fd)); + + ATF_REQUIRE_EQ(-1, rump_sys_mkdir("/td2/subdir", 0777)); + ATF_REQUIRE_EQ(ENOSPC, errno); + + RL(fd = rump_sys_open("/td2/newfile", O_CREAT | O_WRONLY, 0600)); + RL(rump_sys_close(fd)); +} + + +ATF_TC(doom_counter_basic); +ATF_TC_HEAD(doom_counter_basic, tc) +{ + atf_tc_set_md_var(tc, "descr", "COUNT mode allows N ops then fails"); +} +ATF_TC_BODY(doom_counter_basic, tc) +{ + struct fullfs_ctl fc; + int fd, i; + + setup_fullfs(); + xput_tfile("/td1/testfile", MSTR, sizeof(MSTR)); + + fullfs_setstate(FULLFS_MODE_COUNT, FULLFS_OP_ALL, ENOSPC, 0, 3); + + for (i = 0; i < 3; i++) { + RL(fd = rump_sys_open("/td2/testfile", O_WRONLY)); + ATF_REQUIRE_EQ((ssize_t)sizeof(MSTR), rump_sys_write(fd, MSTR, sizeof(MSTR))); + RL(rump_sys_close(fd)); + } + + RL(fd = rump_sys_open("/td2/testfile", O_WRONLY)); + ATF_REQUIRE_EQ(-1, rump_sys_write(fd, MSTR, sizeof(MSTR))); + ATF_REQUIRE_EQ(ENOSPC, errno); + RL(rump_sys_close(fd)); + + fullfs_getstate(&fc); + ATF_REQUIRE_EQ(0, fc.fc_doom); +} + + +ATF_TC(doom_counter_opmask); +ATF_TC_HEAD(doom_counter_opmask, tc) +{ + atf_tc_set_md_var(tc, "descr", "COUNT mode only decrements for masked ops"); +} +ATF_TC_BODY(doom_counter_opmask, tc) +{ + int fd; + + setup_fullfs(); + xput_tfile("/td1/testfile", MSTR, sizeof(MSTR)); + + fullfs_setstate(FULLFS_MODE_COUNT, FULLFS_OP_WRITE | FULLFS_OP_CREATE, ENOSPC, 0, 2); + + RL(rump_sys_mkdir("/td1/subdir", 0777)); + RL(rump_sys_mkdir("/td2/subdir", 0777)); + + RL(fd = rump_sys_open("/td2/testfile", O_WRONLY)); + ATF_REQUIRE_EQ((ssize_t)sizeof(MSTR), rump_sys_write(fd, MSTR, sizeof(MSTR))); + RL(rump_sys_close(fd)); + + RL(fd = rump_sys_open("/td2/newfile", O_CREAT | O_WRONLY, 0600)); + RL(rump_sys_close(fd)); + + RL(fd = rump_sys_open("/td2/testfile", O_WRONLY)); + ATF_REQUIRE_EQ(-1, rump_sys_write(fd, MSTR, sizeof(MSTR))); + ATF_REQUIRE_EQ(ENOSPC, errno); + RL(rump_sys_close(fd)); +} + + +ATF_TC(doom_counter_midstream); +ATF_TC_HEAD(doom_counter_midstream, tc) +{ + atf_tc_set_md_var(tc, "descr", "COUNT doom=1 simulates disk filling mid-save"); +} +ATF_TC_BODY(doom_counter_midstream, tc) +{ + int fd; + + setup_fullfs(); + xput_tfile("/td1/testfile", MSTR, sizeof(MSTR)); + + fullfs_setstate(FULLFS_MODE_COUNT, FULLFS_OP_ALL, ENOSPC, 0, 1); + + RL(fd = rump_sys_open("/td2/testfile", O_WRONLY)); + ATF_REQUIRE_EQ((ssize_t)sizeof(MSTR), rump_sys_write(fd, MSTR, sizeof(MSTR))); + ATF_REQUIRE_EQ(-1, rump_sys_write(fd, MSTR, sizeof(MSTR))); + ATF_REQUIRE_EQ(ENOSPC, errno); + RL(rump_sys_close(fd)); +} + + +ATF_TC(byte_counter_basic); +ATF_TC_HEAD(byte_counter_basic, tc) +{ + atf_tc_set_md_var(tc, "descr", "BYTES mode allows N bytes then fails"); +} +ATF_TC_BODY(byte_counter_basic, tc) +{ + char buf[10]; + int fd; + + setup_fullfs(); + xput_tfile("/td1/testfile", MSTR, sizeof(MSTR)); + + fullfs_setstate(FULLFS_MODE_BYTES, FULLFS_OP_ALL, ENOSPC, 0, 20); + + RL(fd = rump_sys_open("/td2/testfile", O_WRONLY)); + + memset(buf, 'A', sizeof(buf)); + ATF_REQUIRE_EQ(10, rump_sys_write(fd, buf, 10)); + ATF_REQUIRE_EQ(10, rump_sys_write(fd, buf, 10)); + ATF_REQUIRE_EQ(-1, rump_sys_write(fd, buf, 1)); + ATF_REQUIRE_EQ(ENOSPC, errno); + + RL(rump_sys_close(fd)); +} + + +ATF_TC(byte_counter_oversize); +ATF_TC_HEAD(byte_counter_oversize, tc) +{ + atf_tc_set_md_var(tc, "descr", "BYTES rejects oversized write without consuming budget"); +} +ATF_TC_BODY(byte_counter_oversize, tc) +{ + struct fullfs_ctl fc; + char buf[10]; + int fd; + + setup_fullfs(); + xput_tfile("/td1/testfile", MSTR, sizeof(MSTR)); + + fullfs_setstate(FULLFS_MODE_BYTES, FULLFS_OP_ALL, ENOSPC, 0, 5); + + RL(fd = rump_sys_open("/td2/testfile", O_WRONLY)); + memset(buf, 'A', sizeof(buf)); + ATF_REQUIRE_EQ(-1, rump_sys_write(fd, buf, 10)); + ATF_REQUIRE_EQ(ENOSPC, errno); + RL(rump_sys_close(fd)); + + fullfs_getstate(&fc); + ATF_REQUIRE_EQ(5, fc.fc_doom); +} + + +ATF_TC(byte_counter_nonwrite); +ATF_TC_HEAD(byte_counter_nonwrite, tc) +{ + atf_tc_set_md_var(tc, "descr", "BYTES mode does not consume budget for non-write ops"); +} +ATF_TC_BODY(byte_counter_nonwrite, tc) +{ + struct fullfs_ctl fc; + int fd; + + setup_fullfs(); + + fullfs_setstate(FULLFS_MODE_BYTES, FULLFS_OP_ALL, ENOSPC, 0, 1); + + RL(fd = rump_sys_open("/td2/newfile", O_CREAT | O_WRONLY, 0600)); + RL(rump_sys_close(fd)); + + RL(rump_sys_mkdir("/td2/subdir", 0777)); + + fullfs_getstate(&fc); + ATF_REQUIRE_EQ(1, fc.fc_doom); +} + + +ATF_TC(random_basic); +ATF_TC_HEAD(random_basic, tc) +{ + atf_tc_set_md_var(tc, "descr", "RANDOM mode fails at approximately the configured rate"); +} +ATF_TC_BODY(random_basic, tc) +{ + int fd, i, ok; + + setup_fullfs(); + xput_tfile("/td1/testfile", MSTR, sizeof(MSTR)); + + fullfs_setstate(FULLFS_MODE_RANDOM, FULLFS_OP_WRITE, ENOSPC, 50, 0); + + ok = 0; + for (i = 0; i < 1000; i++) { + RL(fd = rump_sys_open("/td2/testfile", O_WRONLY)); + if (rump_sys_write(fd, MSTR, sizeof(MSTR)) != -1) + ok++; + RL(rump_sys_close(fd)); + } + + ATF_REQUIRE_MSG(ok >= 400 && ok <= 600, "expected 400-600 successes, got %d", ok); +} + + +ATF_TC(random_error_code); +ATF_TC_HEAD(random_error_code, tc) +{ + atf_tc_set_md_var(tc, "descr", "RANDOM mode uses configured error code"); +} +ATF_TC_BODY(random_error_code, tc) +{ + int fd, i; + + setup_fullfs(); + xput_tfile("/td1/testfile", MSTR, sizeof(MSTR)); + + fullfs_setstate(FULLFS_MODE_RANDOM, FULLFS_OP_WRITE, EIO, 99, 0); + + for (i = 0; i < 1000; i++) { + RL(fd = rump_sys_open("/td2/testfile", O_WRONLY)); + if (rump_sys_write(fd, MSTR, sizeof(MSTR)) == -1) { + ATF_REQUIRE_EQ(EIO, errno); + RL(rump_sys_close(fd)); + return; + } + RL(rump_sys_close(fd)); + } + atf_tc_fail("no failure in 1000 writes at 99%% rate"); +} + + +ATF_TC(setstate_invalid); +ATF_TC_HEAD(setstate_invalid, tc) +{ + atf_tc_set_md_var(tc, "descr", "SETSTATE rejects invalid parameters"); +} +ATF_TC_BODY(setstate_invalid, tc) +{ + struct fullfs_ctl fc; + int fd; + + setup_fullfs(); + + RL(fd = rump_sys_open("/td2", O_RDONLY)); + +#define EXPECT_EINVAL() do { \ + ATF_REQUIRE_EQ(-1, rump_sys_ioctl(fd, FULLFS_SETSTATE, &fc)); \ + ATF_REQUIRE_EQ(EINVAL, errno); \ +} while (0) + + memset(&fc, 0, sizeof(fc)); + fc.fc_mode = FULLFS_MODE_RANDOM + 1; + fc.fc_error = ENOSPC; + fc.fc_opmask = FULLFS_OP_ALL; + EXPECT_EINVAL(); + + memset(&fc, 0, sizeof(fc)); + fc.fc_mode = FULLFS_MODE_FAIL; + fc.fc_error = 0; + fc.fc_opmask = FULLFS_OP_ALL; + EXPECT_EINVAL(); + + memset(&fc, 0, sizeof(fc)); + fc.fc_mode = FULLFS_MODE_FAIL; + fc.fc_error = -1; + fc.fc_opmask = FULLFS_OP_ALL; + EXPECT_EINVAL(); + + memset(&fc, 0, sizeof(fc)); + fc.fc_mode = FULLFS_MODE_FAIL; + fc.fc_error = ENOSPC; + fc.fc_opmask = FULLFS_OP_ALL + 1; + EXPECT_EINVAL(); + + memset(&fc, 0, sizeof(fc)); + fc.fc_mode = FULLFS_MODE_COUNT; + fc.fc_error = ENOSPC; + fc.fc_opmask = FULLFS_OP_ALL; + fc.fc_doom = 0; + EXPECT_EINVAL(); + + memset(&fc, 0, sizeof(fc)); + fc.fc_mode = FULLFS_MODE_BYTES; + fc.fc_error = ENOSPC; + fc.fc_opmask = FULLFS_OP_ALL; + fc.fc_doom = -1; + EXPECT_EINVAL(); + + memset(&fc, 0, sizeof(fc)); + fc.fc_mode = FULLFS_MODE_RANDOM; + fc.fc_error = ENOSPC; + fc.fc_opmask = FULLFS_OP_ALL; + fc.fc_rate = 0; + EXPECT_EINVAL(); + + memset(&fc, 0, sizeof(fc)); + fc.fc_mode = FULLFS_MODE_RANDOM; + fc.fc_error = ENOSPC; + fc.fc_opmask = FULLFS_OP_ALL; + fc.fc_rate = 100; + EXPECT_EINVAL(); + +#undef EXPECT_EINVAL + + RL(rump_sys_close(fd)); +} + + +ATF_TC(ioctl_on_regular_file); +ATF_TC_HEAD(ioctl_on_regular_file, tc) +{ + atf_tc_set_md_var(tc, "descr", "ioctl works on regular files, not just mountpoint"); +} +ATF_TC_BODY(ioctl_on_regular_file, tc) +{ + struct fullfs_ctl fc; + int fd; + + setup_fullfs(); + xput_tfile("/td1/testfile", MSTR, sizeof(MSTR)); + + RL(fd = rump_sys_open("/td2/testfile", O_RDONLY)); + + RL(rump_sys_ioctl(fd, FULLFS_GETSTATE, &fc)); + ATF_REQUIRE_EQ(FULLFS_MODE_FAIL, fc.fc_mode); + + memset(&fc, 0, sizeof(fc)); + fc.fc_mode = FULLFS_MODE_PASS; + fc.fc_error = ENOSPC; + fc.fc_opmask = FULLFS_OP_ALL; + RL(rump_sys_ioctl(fd, FULLFS_SETSTATE, &fc)); + + RL(rump_sys_close(fd)); +} + + +ATF_TC(statvfs_follows_mode); +ATF_TC_HEAD(statvfs_follows_mode, tc) +{ + atf_tc_set_md_var(tc, "descr", "statvfs reports free space based on current mode"); +} +ATF_TC_BODY(statvfs_follows_mode, tc) +{ + struct statvfs sb; + + setup_fullfs(); + + RL(rump_sys_statvfs1("/td2", &sb, ST_WAIT)); + ATF_REQUIRE_EQ(0, sb.f_bfree); + + fullfs_setstate(FULLFS_MODE_PASS, FULLFS_OP_ALL, ENOSPC, 0, 0); + RL(rump_sys_statvfs1("/td2", &sb, ST_WAIT)); + ATF_REQUIRE_MSG(sb.f_bfree > 0, "expected f_bfree > 0 in PASS mode"); + + fullfs_setstate(FULLFS_MODE_FAIL, FULLFS_OP_ALL, ENOSPC, 0, 0); + RL(rump_sys_statvfs1("/td2", &sb, ST_WAIT)); + ATF_REQUIRE_EQ(0, sb.f_bfree); +} + + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, read_succeeds); @@ -199,5 +759,24 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, symlink_fails); ATF_TP_ADD_TC(tp, remove_succeeds); ATF_TP_ADD_TC(tp, statvfs_shows_full); + ATF_TP_ADD_TC(tp, getstate_initial); + ATF_TP_ADD_TC(tp, toggle_pass_fail); + ATF_TP_ADD_TC(tp, error_code); + ATF_TP_ADD_TC(tp, error_roundtrip); + ATF_TP_ADD_TC(tp, opmask_write_only); + ATF_TP_ADD_TC(tp, opmask_create_only); + ATF_TP_ADD_TC(tp, opmask_none); + ATF_TP_ADD_TC(tp, opmask_multiple); + ATF_TP_ADD_TC(tp, doom_counter_basic); + ATF_TP_ADD_TC(tp, doom_counter_opmask); + ATF_TP_ADD_TC(tp, doom_counter_midstream); + ATF_TP_ADD_TC(tp, byte_counter_basic); + ATF_TP_ADD_TC(tp, byte_counter_oversize); + ATF_TP_ADD_TC(tp, byte_counter_nonwrite); + ATF_TP_ADD_TC(tp, random_basic); + ATF_TP_ADD_TC(tp, random_error_code); + ATF_TP_ADD_TC(tp, setstate_invalid); + ATF_TP_ADD_TC(tp, ioctl_on_regular_file); + ATF_TP_ADD_TC(tp, statvfs_follows_mode); return atf_no_error(); }