Download raw body.
gefs vs ioctls
9front doesn't have ioctls, instead we have a synthetic
ctl file that accepts commands. For example:
% cat /adm/status
gefs p9
usage 0.00% 0.50MiB 223.57GiB
conf retain '' '60@m 24@h @d'
fork adm
snap adm@day.2026.11.02_00:47:54
snap adm@hour.2026.11.02_00:47:54
snap adm@minute.2026.11.02_00:47:54
snap empty
fork main
or
echo snap main main.cur > /adm/ctl
For the writes, it's pretty simple to map them to
ioctls:
struct gefs_snap {
char gs_old[256];
char gs_new[256];
};
struct gefs_retain {
char gr_snap[256];
char gr_sched[256];
};
#define GEFS_SNAP _IOW('G', 1, struct gefs_snap)
#define GEFS_FORK _IOW('G', 2, struct gefs_snap)
#define GEFS_DEL _IOW('G', 3, struct gefs_snap)
#define GEFS_RETAIN _IOW('G', 4, struct gefs_retain)
#define GEFS_BALLAST _IOW('G', 5, int) /* 0 or 1 for enabling ballast */
#define GEFS_DEBUG _IOW('G', 6, int) /* debug level */
And we can just allow it on any file on a gefs volume:
gefs -f . snap blah
But, for this, we need to pass through ioctl requests
on regular files. and I'm not sure how I feel about it:
diff 0620ae0b52bc2ff8f33f33434b789f310a391745 uncommitted
--- a/sys/kern/vfs_vnops.c
+++ b/sys/kern/vfs_vnops.c
@@ -516,9 +516,9 @@
struct vattr vattr;
int error = ENOTTY;
+ com, vp->v_type, VREG, VDIR);
KERNEL_LOCK();
switch (vp->v_type) {
-
case VREG:
case VDIR:
if (com == FIONREAD) {
@@ -526,11 +526,12 @@
if (error)
break;
*(int *)data = vattr.va_size - foffset(fp);
-
- } else if (com == FIOASYNC) /* XXX */
+ break;
+ } else if (com == FIOASYNC) { /* XXX */
error = 0; /* XXX */
- break;
-
+ break;
+ }
+ /* wet floor */
case VFIFO:
case VCHR:
case VBLK:
for reding the status, I'm a bit stuck what the best
option would be, since the output isn't necessarily
staticaly sized.
The best I can come up with is some sort of iteration
over an index, but it feels very race-prone.
ioctl(GEFS_SNAPCOUNT, &nsnap);
for(i = 0; i < nsnap; i++){
ioctl(GEFS_SNAPNAME, i, name);
printf("%s\n", name);
}
Any thoughts on a better aproach?
Appendix, the current implementation (only lightly
tested, not yet pushed)
---
diff 279e65a41d45eadcc2fa793ae23b7c82ef328062 8aa73e9954ab329ac5a589618dff9ba768dea4e0
--- /dev/null
+++ b/sbin/gefs/Makefile
@@ -1,0 +1,11 @@
+# $OpenBSD: Makefile,v 1.4 2001/01/10 20:02:29 deraadt Exp $
+
+PROG= gefs
+SRCS= gefs.c
+MAN= gefs.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${MOUNT}
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
--- /dev/null
+++ b/sbin/gefs/gefs.8
@@ -1,0 +1,65 @@
+.\" Copyright (c) 2026 Ori Bernstein
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in
+.\" the documentation and/or other materials provided with the
+.\" distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
+.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) 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 $Mdocdate: Oct 2 2026 $
+.Dt GEFS 8
+.Os
+.Sh NAME
+.Nm gefs
+.Nd Administration commands for GEFS
+.Sh SYNOPSIS
+.Nm gefs
+.Op Ar debug
+.Op Ar usereserve
+.Op Ar snap
+.Op Ar fork
+.Op Ar delsnap
+.Ek
+.Sh DESCRIPTION
+The
+.Nm
+allows access to gefs administrative operations such as snapshotting and
+debugging.
+The commands are as follows:
+.It Ar debug level
+Enable debugging at given level
+.It Ar usereserve [0|1]
+GEFS may need space in order to delete files.
+As a result, some space is held in reserve so that a full disk is recoverable.
+This releases the reserved space.
+If you run out of disk space with this option enabled,
+no files can be deleted.
+.It Ar snap old new
+Takes a snapshot
+.Ar new
+based off the contents of
+.Ar old.
+.It Ar fork old new
+Takes a mutable fork
+.Ar new
+based off the contents of
+.Ar old.
+.It Ar delsnap snap
+Removes the snapshot or fork
+.Ar snap
--- /dev/null
+++ b/sbin/gefs/gefs.c
@@ -1,0 +1,128 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2026 Ori Bernstein <ori@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/gefs.h>
+#include <sys/ioctl.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <fcntl.h>
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: gefs [-f fs] debug [0|1]\n");
+ fprintf(stderr, " gefs [-f fs] snap old new\n");
+ fprintf(stderr, " gefs [-f fs] fork old new\n");
+ fprintf(stderr, " gefs [-f fs] retain fork timespec\n");
+ fprintf(stderr, " gefs [-f fs] usereserve\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct gefs_snap gs;
+ struct gefs_retain gr;
+ int r, v, ch, fd;
+ const char *e;
+
+ fd = -1;
+ while ((ch = getopt(argc, argv, "o:")) != -1) {
+ switch (ch) {
+ case 'f':
+ if ((fd = open(optarg, O_RDONLY)) == -1)
+ err(1, "open %s:", optarg);
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (fd == -1) {
+ if ((fd = open(".", O_RDONLY)) == -1)
+ err(1, "open .:");
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ usage();
+
+ if (strcmp(argv[0], "debug") == 0) {
+ if (argc != 2)
+ usage();
+ v = strtonum(argv[1], 0, 1, &e);
+ if (e != NULL) {
+ fprintf(stderr, "debug: invalid argument: %s", e);
+ exit(1);
+ }
+ r = ioctl(fd, GEFS_DEBUG, &v);
+ } else if (strcmp(argv[0], "usereserve") == 0) {
+ if (argc != 3)
+ usage();
+ v = strtonum(argv[1], 0, 1, &e);
+ if (e != NULL) {
+ fprintf(stderr, "debug: invalid argument: %s", e);
+ exit(1);
+ }
+ r = ioctl(fd, GEFS_BALLAST, &v);
+ } else if (strcmp(argv[0], "retain") == 0) {
+ if (argc != 2)
+ usage();
+ if (strlcpy(gr.gr_snap, argv[1], sizeof(gr.gr_snap)) >= sizeof(gr.gr_snap))
+ errx(1, "overlong snap name");
+ if (strlcpy(gr.gr_sched, argv[2], sizeof(gr.gr_sched)) >= sizeof(gr.gr_sched))
+ errx(1, "overlong schedule");
+ r = ioctl(fd, GEFS_RETAIN, &gr);
+ } else if (strcmp(argv[0], "snap") == 0) {
+ if (argc != 3)
+ usage();
+ if (strlcpy(gs.gs_old, argv[1], sizeof(gs.gs_old)) >= sizeof(gs.gs_old))
+ errx(1, "overlong old snap name");
+ if (strlcpy(gs.gs_new, argv[2], sizeof(gs.gs_new)) >= sizeof(gs.gs_new))
+ errx(1, "overlong new snap name");
+ r = ioctl(fd, GEFS_SNAP, &gs);
+ } else if (strcmp(argv[0], "fork") == 0) {
+ if (argc != 3)
+ usage();
+ if (strlcpy(gs.gs_old, argv[1], sizeof(gs.gs_old)) >= sizeof(gs.gs_old))
+ errx(1, "overlong old snap name");
+ if (strlcpy(gs.gs_new, argv[2], sizeof(gs.gs_new)) >= sizeof(gs.gs_new))
+ errx(1, "overlong new snap name");
+ r = ioctl(fd, GEFS_FORK, &gs);
+ } else if (strcmp(argv[0], "delsnap") == 0) {
+ if (argc != 2)
+ usage();
+ if (strlcpy(gs.gs_old, argv[1], sizeof(gs.gs_old)) >= sizeof(gs.gs_old))
+ errx(1, "overlong old snap name");
+ gs.gs_new[0] = 0;
+ r = ioctl(fd, GEFS_DEL, &gs);
+
+ }
+ if (r == -1)
+ err(1, "ioctl %s:", argv[0]);
+ return 0;
+}
--- a/sys/gefs/dat.h
+++ b/sys/gefs/dat.h
@@ -11,7 +11,6 @@
typedef struct Dent Dent;
typedef struct Dlist Dlist;
typedef struct Errctx Errctx;
-typedef struct Fmsg Fmsg;
typedef struct Gefs Gefs;
typedef struct Key Key;
typedef struct Kvp Kvp;
@@ -455,7 +454,7 @@
char new[128];
int flag;
char delete;
- Fmsg *m;
+ void *rz;
};
struct { /* AOsetcfg AOclrcfg */
char snap[128];
@@ -585,6 +584,8 @@
struct rwlock quitlk;
short quitrz;
+ short ioctlrz;
+
struct rwlock mountlk;
Aptr mounts; /* Mount* */
Mount *snapmnt;
@@ -607,6 +608,7 @@
Along rdonly;
int noauth;
int usereserve;
+ int debug;
/* slow block io */
struct rwlock blklk[32];
--- a/sys/gefs/fns.h
+++ b/sys/gefs/fns.h
@@ -66,6 +66,7 @@
void gefs_clunkdent(Mount*, Dent*);
void gefs_touch(Dent*, Msg*);
int gefs_vget(struct mount*, ino_t, struct vnode**);
+int gefs_ioctl(void*);
Tree* gefs_updatesnap(Tree*, char*, int);
int gefs_tagsnap(Tree*, char*, int);
--- /dev/null
+++ b/sys/gefs/ioctl.c
@@ -1,0 +1,325 @@
+/*
+ * Copyright (c) 2026
+ * Ori Bernstein. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <sys/lock.h>
+#include <sys/rwlock.h>
+#include <sys/mount.h>
+#include <sys/malloc.h>
+#include <sys/pool.h>
+#include <sys/specdev.h>
+#include <sys/gefs.h>
+#include <miscfs/fifofs/fifo.h>
+
+#include "u.h"
+#include "avl.h"
+
+#include "dat.h"
+#include "fns.h"
+
+#ifdef NOTYET
+static void
+fmtdf(Fmt *fmt)
+{
+ char *units[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", nil};
+ vlong size, used;
+ double hsize, hused;
+ Arena *a;
+ int i, us, uu;
+
+ size = 0;
+ used = 0;
+ for(i = 0; i < fs->narena; i++){
+ a = &fs->arenas[i];
+ qlock(a);
+ size += a->size;
+ used += a->used;
+ qunlock(a);
+ }
+ hsize = size;
+ hused = used;
+ for(us = 0; us < nelem(units)-1 && hsize >= 500 ; us++)
+ hsize /= 1024;
+ for(uu = 0; uu < nelem(units)-1 && hused >= 500 ; uu++)
+ hused /= 1024;
+ fmtprint(fmt, "usage \t%.2f%%\t%.2f%s\t%.2f%s\n",
+ 100.0*used/size,
+ hused, units[uu],
+ hsize, units[us]);
+}
+
+static void
+showconf(Fmt *fmt, Tree *t, char *name)
+{
+ char pfx[1];
+ int shown;
+ Scan s;
+
+ shown = 0;
+ pfx[0] = Kconf;
+ btnewscan(&s, pfx, 1);
+ btenter(t, &s);
+ while(1){
+ if(!btnext(&s, &s.kv))
+ break;
+ fmtprint(fmt, "conf %q %.*q %.*q\n",
+ name, (int)s.kv.nk-1, s.kv.k+1, (int)s.kv.nv, s.kv.v);
+ shown = 1;
+ }
+ btexit(&s);
+ if(name[0] == 0 && !shown)
+ fmtprint(fmt, "conf retain '' '60@m 24@h @d'\n");
+}
+
+static void
+iterconf(Fmt *fmt, int flg, char *name)
+{
+ Mount *mnt;
+ Tree *t;
+
+ if((flg & Lmut) && !waserror()){
+ mnt = getmount(name);
+ t = agetp(&mnt->root);
+ showconf(fmt, t, name);
+ clunkmount(mnt);
+ poperror();
+ }
+}
+
+static void
+showsnap(Fmt *fmt, int flg, char *name)
+{
+ if(flg & Lmut)
+ fmtprint(fmt, "fork %q\n", name);
+ else
+ fmtprint(fmt, "snap %q\n", name);
+}
+
+static void
+itersnaps(Fmt *fmt, void(*show)(Fmt*, int, char*))
+{
+ char pfx[Snapsz], name[Keymax];
+ Scan s;
+ uint flg;
+ int sz;
+
+ pfx[0] = Klabel;
+ sz = 1;
+ btnewscan(&s, pfx, sz);
+ btenter(&fs->snap, &s);
+ while(1){
+ if(!btnext(&s, &s.kv))
+ break;
+ flg = UNPACK32(s.kv.v+1+8);
+ memcpy(name, s.kv.k+1, s.kv.nk-1);
+ name[s.kv.nk-1] = 0;
+ show(fmt, flg, name);
+ }
+ btexit(&s);
+}
+
+void
+readstatus(Fmsg *m, Fid *f, Fcall *r)
+{
+ Fmt fmt;
+ int n;
+
+ if(f->aux == nil || m->offset == 0){
+ free(f->aux);
+ f->aux = nil;
+ if((fmtstrinit(&fmt)) == -1)
+ error("%r");
+ if(waserror()){
+ free(fmtstrflush(&fmt));
+ nexterror();
+ }
+ fmtprint(&fmt, "gefs p9\n");
+ fmtdf(&fmt);
+ showconf(&fmt, &fs->snap, "");
+ itersnaps(&fmt, iterconf);
+ itersnaps(&fmt, showsnap);
+ f->aux = fmtstrflush(&fmt);
+ poperror();
+ }
+ if(f->aux == nil)
+ error(Efs);
+
+ n = strlen(f->aux);
+ if(m->offset >= n)
+ r->count = 0;
+ else if(m->offset + m->count > n)
+ r->count = n - m->offset;
+ else
+ r->count = m->count;
+ memcpy(r->data, (char*)f->aux+m->offset, r->count);
+}
+#endif
+
+void
+gefs_asend(Gefs *fs, int op, void *rz)
+{
+ Amsg *a;
+
+ a = emalloc(sizeof(Amsg), 1);
+ a->op = op;
+ a->rz = rz;
+ gefs_chsend(fs->admchan, a);
+ if (rz != NULL) {
+ }
+}
+
+int
+gefs_setretain(Gefs *fs, char *snap, char *spec)
+{
+ Amsg *a;
+
+ a = emalloc(sizeof(Amsg), 1);
+ a->rz = &fs->ioctlrz;
+ if(strlcpy(a->key, "retain", sizeof(a->key)) >= sizeof(a->key))
+ panic("wtf");
+ if(strcmp(snap, "-") != 0)
+ if(strlcpy(a->snap, snap, sizeof(a->snap)))
+ goto error;
+ if(strcmp(spec, "default") == 0){
+ a->op = AOclrcfg;
+ a->val[0] = 0;
+ }else{
+ a->op = AOsetcfg;
+ if(strlcpy(a->val, spec, sizeof(a->val)) >= sizeof(a->val))
+ goto error;
+ }
+ gefs_chsend(fs->admchan, a);
+ return 0;
+error:
+ free(a, M_TEMP, sizeof(Amsg));
+ return EINVAL;
+}
+
+int
+gefs_clrsnap(Gefs *fs, char *tag)
+{
+ Amsg *a;
+
+ a = emalloc(sizeof(Amsg), 1);
+ a->op = AOsnap;
+ a->delete = 1;
+ a->rz = &fs->ioctlrz;
+ if(strlcpy(a->old, tag, sizeof(a->old)) >= sizeof(a->old)){
+ free(a, M_TEMP, sizeof(Amsg));
+ return EINVAL;
+ }
+ gefs_chsend(fs->admchan, a);
+ return 0;
+}
+
+int
+gefs_takesnap(Gefs *fs, char *old, char *new, int flag)
+{
+ Amsg *a;
+
+ a = emalloc(sizeof(Amsg), 1);
+ a->op = AOsnap;
+ a->delete = 0;
+ a->flag = flag;
+ a->rz = &fs->ioctlrz;
+ if(strlcpy(a->old, old, sizeof(a->old)) >= sizeof(a->old))
+ goto error;
+ if(strlcpy(a->new, new, sizeof(a->new)) >= sizeof(a->new))
+ goto error;
+ gefs_chsend(fs->admchan, a);
+ return 0;
+error:
+ free(a, M_TEMP, sizeof(Amsg));
+ return EINVAL;
+}
+
+int
+gefs_ioctl(void *p)
+{
+ struct vop_ioctl_args *ap = p;
+ struct gefs_snap *gs;
+ struct gefs_retain *gr;
+ int v, err, res;
+
+ res = -1;
+ rw_enter(&fs->mutlk, RW_WRITE);
+ switch(ap->a_command){
+ default:
+ return EINVAL;
+ case GEFS_SNAP:
+ case GEFS_FORK:
+ case GEFS_DEL:
+ gs = ap->a_data;
+ if(memchr(gs->gs_old, 0, sizeof(gs->gs_old)) == NULL)
+ return EINVAL;
+ if(memchr(gs->gs_new, 0, sizeof(gs->gs_new)) == NULL)
+ return EINVAL;
+ switch(ap->a_command){
+ case GEFS_SNAP:
+ err = gefs_takesnap(fs, gs->gs_old, gs->gs_new, 0);
+ break;
+ case GEFS_FORK:
+ err = gefs_takesnap(fs, gs->gs_old, gs->gs_new, Lmut);
+ break;
+ case GEFS_DEL:
+ err = gefs_clrsnap(fs, gs->gs_old);
+ break;
+ }
+ if(err != 0)
+ return err;
+ rwsleep(&fs->ioctlrz, &fs->mutlk, IPL_NONE, "cmdwait", 0);
+ break;
+ case GEFS_RETAIN:
+ gr = ap->a_data;
+ if(memchr(gr->gr_snap, 0, sizeof(gr->gr_snap)) == NULL)
+ return EINVAL;
+ if(memchr(gr->gr_sched, 0, sizeof(gr->gr_sched)) == NULL)
+ return EINVAL;
+ if ((err = gefs_setretain(fs, gr->gr_snap, gr->gr_sched)) != 0)
+ return err;
+ rwsleep(&fs->ioctlrz, &fs->mutlk, IPL_NONE, "cmdwait", 0);
+ break;
+ case GEFS_BALLAST:
+ v = *(int*)ap->a_data;
+ if(v & ~1)
+ return EINVAL;
+ fs->usereserve = v;
+ break;
+ case GEFS_DEBUG:
+ fs->debug = *(int*)ap->a_data;
+ break;
+ }
+ rw_exit(&fs->mutlk);
+ return 0;
+}
--- a/sys/gefs/sweep.c
+++ b/sys/gefs/sweep.c
@@ -561,22 +561,11 @@
break;
}
Next:
-// delayed responses; do we have any?
-// if(am->m != NULL){
-// switch(am->m->type){
-// case Twstat:
-// r.type = Rwstat;
-// break;
-// case Twrite:
-// r.type = Rwrite;
-// r.count = am->m->count;
-// break;
-// default:
-// abort();
-// }
-// respond(am->m, &r);
-// }
-// assert(estacksz() == 0);
+ if(am->rz != NULL){
+ rw_enter(&fs->mutlk, RW_WRITE);
+ wakeup(am->rz);
+ rw_exit(&fs->mutlk);
+ }
gefs_freeamsg(am);
}
}
--- a/sys/gefs/vnops.c
+++ b/sys/gefs/vnops.c
@@ -1643,7 +1643,7 @@
.vop_setattr = gefs_setattr,
.vop_read = gefs_read,
.vop_write = gefs_write,
- .vop_ioctl = (void *)eopnotsupp,
+ .vop_ioctl = gefs_ioctl,
.vop_kqfilter = (void *)eopnotsupp,
.vop_revoke = vop_generic_revoke,
.vop_fsync = gefs_fsync,
--- a/sys/kern/vfs_vnops.c
+++ b/sys/kern/vfs_vnops.c
@@ -516,9 +516,9 @@
struct vattr vattr;
int error = ENOTTY;
+ com, vp->v_type, VREG, VDIR);
KERNEL_LOCK();
switch (vp->v_type) {
-
case VREG:
case VDIR:
if (com == FIONREAD) {
@@ -526,11 +526,12 @@
if (error)
break;
*(int *)data = vattr.va_size - foffset(fp);
-
- } else if (com == FIOASYNC) /* XXX */
+ break;
+ } else if (com == FIOASYNC) { /* XXX */
error = 0; /* XXX */
- break;
-
+ break;
+ }
+ /* wet floor */
case VFIFO:
case VCHR:
case VBLK:
--- /dev/null
+++ b/sys/sys/gefs.h
@@ -1,0 +1,24 @@
+#ifndef _SYS_GEFS_H_
+#define _SYS_GEFS_H_
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/ioccom.h>
+
+struct gefs_snap {
+ char gs_old[256];
+ char gs_new[256];
+};
+
+struct gefs_retain {
+ char gr_snap[256];
+ char gr_sched[256];
+};
+
+#define GEFS_SNAP _IOW('G', 1, struct gefs_snap)
+#define GEFS_FORK _IOW('G', 2, struct gefs_snap)
+#define GEFS_DEL _IOW('G', 3, struct gefs_snap)
+#define GEFS_RETAIN _IOW('G', 4, struct gefs_retain)
+#define GEFS_BALLAST _IOW('G', 5, int) /* 0 or 1 for enabling ballast */
+#define GEFS_DEBUG _IOW('G', 6, int) /* debug level */
+
+#endif
--
⑨
gefs vs ioctls