1*95d71e0dSryo /*	$NetBSD: sysctlfs.c,v 1.21 2023/04/02 18:23:02 ryo Exp $	*/
2ba4e596bSpooka 
304c15978Spooka /*-
4ba4e596bSpooka  * Copyright (c) 2006, 2007  Antti Kantee.  All Rights Reserved.
5ba4e596bSpooka  *
6ba4e596bSpooka  * Redistribution and use in source and binary forms, with or without
7ba4e596bSpooka  * modification, are permitted provided that the following conditions
8ba4e596bSpooka  * are met:
9ba4e596bSpooka  * 1. Redistributions of source code must retain the above copyright
10ba4e596bSpooka  *    notice, this list of conditions and the following disclaimer.
11ba4e596bSpooka  * 2. Redistributions in binary form must reproduce the above copyright
12ba4e596bSpooka  *    notice, this list of conditions and the following disclaimer in the
13ba4e596bSpooka  *    documentation and/or other materials provided with the distribution.
14ba4e596bSpooka  *
15ba4e596bSpooka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16ba4e596bSpooka  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17ba4e596bSpooka  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18ba4e596bSpooka  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19ba4e596bSpooka  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20ba4e596bSpooka  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21ba4e596bSpooka  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22ba4e596bSpooka  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23ba4e596bSpooka  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24ba4e596bSpooka  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25ba4e596bSpooka  * SUCH DAMAGE.
26ba4e596bSpooka  */
27ba4e596bSpooka 
28ba4e596bSpooka /*
29ba4e596bSpooka  * sysctlfs: mount sysctls as a file system tree.  Supports query and
30ba4e596bSpooka  * modify of nodes in the sysctl namespace in addition to namespace
31ba4e596bSpooka  * traversal.
32ba4e596bSpooka  */
33ba4e596bSpooka 
34e0876492Spooka #include <sys/cdefs.h>
35e0876492Spooka #ifndef lint
36*95d71e0dSryo __RCSID("$NetBSD: sysctlfs.c,v 1.21 2023/04/02 18:23:02 ryo Exp $");
37e0876492Spooka #endif /* !lint */
38e0876492Spooka 
39ba4e596bSpooka #include <sys/types.h>
40ba4e596bSpooka #include <sys/sysctl.h>
41ba4e596bSpooka 
423b2f04fbSchristos #include <stdio.h>
43ba4e596bSpooka #include <assert.h>
44ba4e596bSpooka #include <err.h>
45ba4e596bSpooka #include <errno.h>
46ba4e596bSpooka #include <mntopts.h>
47ba4e596bSpooka #include <paths.h>
48ba4e596bSpooka #include <puffs.h>
49ba4e596bSpooka #include <stdlib.h>
50ba4e596bSpooka #include <string.h>
51ba4e596bSpooka #include <unistd.h>
52ba4e596bSpooka #include <util.h>
53ba4e596bSpooka 
54c7528563Spooka #ifdef RUMP_ACTION
55c7528563Spooka #include <rump/rump.h>
56c7528563Spooka #include <rump/rump_syscalls.h>
57c7528563Spooka 
58c7528563Spooka #define sysctl(a,b,c,d,e,f) rump_sys___sysctl(a,b,c,d,e,f)
59c7528563Spooka #endif
60c7528563Spooka 
61ba4e596bSpooka PUFFSOP_PROTOS(sysctlfs)
62ba4e596bSpooka 
63ba4e596bSpooka struct sfsnode {
64ba4e596bSpooka 	int sysctl_flags;
65ba4e596bSpooka 	ino_t myid;
66ba4e596bSpooka };
67ba4e596bSpooka 
68ba4e596bSpooka #define SFSPATH_DOTDOT 0
69ba4e596bSpooka #define SFSPATH_NORMAL 1
70ba4e596bSpooka 
71ba4e596bSpooka #define N_HIERARCHY 10
72ba4e596bSpooka typedef int SfsName[N_HIERARCHY];
73ba4e596bSpooka 
74ba4e596bSpooka struct sfsfid {
75ba4e596bSpooka 	int len;
76ba4e596bSpooka 	SfsName path;
77ba4e596bSpooka };
78ba4e596bSpooka 
7962234858Spooka static struct sfsnode rn;
8062234858Spooka static SfsName sname_root;
8162234858Spooka static struct timespec fstime;
82ba4e596bSpooka 
8362234858Spooka static ino_t nextid = 3;
8462234858Spooka static mode_t fileperms;
8562234858Spooka static uid_t fileuid;
8662234858Spooka static gid_t filegid;
87ba4e596bSpooka 
8804c15978Spooka static int rflag;
8904c15978Spooka 
90ba4e596bSpooka #define ISADIR(a) ((SYSCTL_TYPE(a->sysctl_flags) == CTLTYPE_NODE))
91c8360f5cSpooka #define SFS_MAXFILE 32768
92ba4e596bSpooka #define SFS_NODEPERDIR 128
93ba4e596bSpooka 
94ba4e596bSpooka static int sysctlfs_domount(struct puffs_usermount *);
95ba4e596bSpooka 
96ba4e596bSpooka /*
97ba4e596bSpooka  * build paths.  doesn't support rename (but neither does the fs)
98ba4e596bSpooka  */
99ba4e596bSpooka static int
sysctlfs_pathbuild(struct puffs_usermount * pu,const struct puffs_pathobj * parent,const struct puffs_pathobj * comp,size_t offset,struct puffs_pathobj * res)100ba4e596bSpooka sysctlfs_pathbuild(struct puffs_usermount *pu,
101ba4e596bSpooka     const struct puffs_pathobj *parent, const struct puffs_pathobj *comp,
102ba4e596bSpooka     size_t offset, struct puffs_pathobj *res)
103ba4e596bSpooka {
104ba4e596bSpooka 	SfsName *sname;
105ba4e596bSpooka 	size_t clen;
106ba4e596bSpooka 
107ba4e596bSpooka 	assert(parent->po_len < N_HIERARCHY); /* code uses +1 */
108ba4e596bSpooka 
109ba4e596bSpooka 	sname = malloc(sizeof(SfsName));
110ba4e596bSpooka 	assert(sname != NULL);
111ba4e596bSpooka 
112ba4e596bSpooka 	clen = parent->po_len;
113ba4e596bSpooka 	if (comp->po_len == SFSPATH_DOTDOT) {
114ba4e596bSpooka 		assert(clen != 0);
115ba4e596bSpooka 		clen--;
116ba4e596bSpooka 	}
117ba4e596bSpooka 
118ba4e596bSpooka 	memcpy(sname, parent->po_path, clen * sizeof(int));
119ba4e596bSpooka 
120ba4e596bSpooka 	res->po_path = sname;
121ba4e596bSpooka 	res->po_len = clen;
122ba4e596bSpooka 
123ba4e596bSpooka 	return 0;
124ba4e596bSpooka }
125ba4e596bSpooka 
126ba4e596bSpooka static int
sysctlfs_pathtransform(struct puffs_usermount * pu,const struct puffs_pathobj * p,const struct puffs_cn * pcn,struct puffs_pathobj * res)127ba4e596bSpooka sysctlfs_pathtransform(struct puffs_usermount *pu,
12804c15978Spooka     const struct puffs_pathobj *p, const struct puffs_cn *pcn,
129ba4e596bSpooka     struct puffs_pathobj *res)
130ba4e596bSpooka {
131ba4e596bSpooka 
132ba4e596bSpooka 	res->po_path = NULL;
133ba4e596bSpooka 	/*
134ba4e596bSpooka 	 * XXX: overload.  prevents us from doing rename, but the fs
135ba4e596bSpooka 	 * (and sysctl(3)) doesn't support it, so no biggie
136ba4e596bSpooka 	 */
137ba4e596bSpooka 	if (PCNISDOTDOT(pcn)) {
138ba4e596bSpooka 		res->po_len = SFSPATH_DOTDOT;
139ba4e596bSpooka 	}else {
140ba4e596bSpooka 		res->po_len = SFSPATH_NORMAL;
141ba4e596bSpooka 	}
142ba4e596bSpooka 
143ba4e596bSpooka 	return 0;
144ba4e596bSpooka }
145ba4e596bSpooka 
146ba4e596bSpooka static int
sysctlfs_pathcmp(struct puffs_usermount * pu,struct puffs_pathobj * po1,struct puffs_pathobj * po2,size_t clen,int checkprefix)147ba4e596bSpooka sysctlfs_pathcmp(struct puffs_usermount *pu, struct puffs_pathobj *po1,
148ba4e596bSpooka     struct puffs_pathobj *po2, size_t clen, int checkprefix)
149ba4e596bSpooka {
150ba4e596bSpooka 
151ba4e596bSpooka 	if (memcmp(po1->po_path, po2->po_path, clen * sizeof(int)) == 0)
152ba4e596bSpooka 		return 0;
153ba4e596bSpooka 	return 1;
154ba4e596bSpooka }
155ba4e596bSpooka 
156ba4e596bSpooka static void
sysctlfs_pathfree(struct puffs_usermount * pu,struct puffs_pathobj * po)157ba4e596bSpooka sysctlfs_pathfree(struct puffs_usermount *pu, struct puffs_pathobj *po)
158ba4e596bSpooka {
159ba4e596bSpooka 
160ba4e596bSpooka 	free(po->po_path);
161ba4e596bSpooka }
162ba4e596bSpooka 
163ba4e596bSpooka static struct puffs_node *
getnode(struct puffs_usermount * pu,struct puffs_pathobj * po,int nodetype)164ba4e596bSpooka getnode(struct puffs_usermount *pu, struct puffs_pathobj *po, int nodetype)
165ba4e596bSpooka {
166ba4e596bSpooka 	struct sysctlnode sn[SFS_NODEPERDIR];
167ba4e596bSpooka 	struct sysctlnode qnode;
168ba4e596bSpooka 	struct puffs_node *pn;
169ba4e596bSpooka 	struct sfsnode *sfs;
170ba4e596bSpooka 	SfsName myname, *sname;
1713cafe960Slukem 	size_t sl, i;
172ba4e596bSpooka 
173ba4e596bSpooka 	/*
174ba4e596bSpooka 	 * Check if we need to create a new in-memory node or if we
175ba4e596bSpooka 	 * already have one for this path.  Shortcut for the rootnode.
176ba4e596bSpooka 	 * Also, memcmp against zero-length would be quite true always.
177ba4e596bSpooka 	 */
178ba4e596bSpooka 	if (po->po_len == 0)
179ba4e596bSpooka 		pn = puffs_getroot(pu);
180ba4e596bSpooka 	else
181ba4e596bSpooka 		pn = puffs_pn_nodewalk(pu, puffs_path_walkcmp, po);
182ba4e596bSpooka 
18353500167Schristos 	if (pn != NULL)
18453500167Schristos 		return pn;
185ba4e596bSpooka 	/*
186ba4e596bSpooka 	 * don't know nodetype?  query...
187ba4e596bSpooka 	 *
188ba4e596bSpooka 	 * XXX1: nothing really guarantees 0 is an invalid nodetype
189ba4e596bSpooka 	 * XXX2: is there really no easier way of doing this?  we
190ba4e596bSpooka 	 *       know the whole mib path
191ba4e596bSpooka 	 */
192ba4e596bSpooka 	if (!nodetype) {
193ba4e596bSpooka 		sname = po->po_path;
194411f3e27Schristos 		memcpy(myname, po->po_path, po->po_len * sizeof(myname[0]));
195ba4e596bSpooka 
196ba4e596bSpooka 		memset(&qnode, 0, sizeof(qnode));
197ba4e596bSpooka 		qnode.sysctl_flags = SYSCTL_VERSION;
198ba4e596bSpooka 		myname[po->po_len-1] = CTL_QUERY;
199ba4e596bSpooka 
200ba4e596bSpooka 		sl = sizeof(sn);
201ba4e596bSpooka 		if (sysctl(myname, po->po_len, sn, &sl,
202ba4e596bSpooka 		    &qnode, sizeof(qnode)) == -1)
203ba4e596bSpooka 			abort();
204ba4e596bSpooka 
205ba4e596bSpooka 		for (i = 0; i < sl / sizeof(struct sysctlnode); i++) {
206ba4e596bSpooka 			 if (sn[i].sysctl_num == (*sname)[po->po_len-1]) {
207ba4e596bSpooka 				nodetype = sn[i].sysctl_flags;
208ba4e596bSpooka 				break;
209ba4e596bSpooka 			}
210ba4e596bSpooka 		}
211ba4e596bSpooka 		if (!nodetype)
212ba4e596bSpooka 			return NULL;
213ba4e596bSpooka 	}
214ba4e596bSpooka 
215411f3e27Schristos 	sfs = emalloc(sizeof(*sfs));
216ba4e596bSpooka 	sfs->sysctl_flags = nodetype;
217ba4e596bSpooka 	sfs->myid = nextid++;
218ba4e596bSpooka 
219ba4e596bSpooka 	pn = puffs_pn_new(pu, sfs);
220ba4e596bSpooka 	assert(pn);
221ba4e596bSpooka 
222ba4e596bSpooka 	return pn;
223ba4e596bSpooka }
224ba4e596bSpooka 
225411f3e27Schristos static void __dead
usage(void)226411f3e27Schristos usage(void)
227411f3e27Schristos {
228411f3e27Schristos 
229*95d71e0dSryo 	fprintf(stderr, "Usage: %s [-o <mntopts>] sysctlfs mountpath\n",
230411f3e27Schristos 	    getprogname());
231411f3e27Schristos 	exit(1);
232411f3e27Schristos }
233411f3e27Schristos 
234ba4e596bSpooka int
main(int argc,char * argv[])235ba4e596bSpooka main(int argc, char *argv[])
236ba4e596bSpooka {
237ba4e596bSpooka 	struct puffs_usermount *pu;
238ba4e596bSpooka 	struct puffs_ops *pops;
239ba4e596bSpooka 	mntoptparse_t mp;
2404b0f2948Spooka 	int mntflags, pflags;
2414b0f2948Spooka 	int detach;
242ba4e596bSpooka 	int ch;
243ba4e596bSpooka 
244ba4e596bSpooka 	setprogname(argv[0]);
245ba4e596bSpooka 
246ba4e596bSpooka 	if (argc < 2)
247411f3e27Schristos 		usage();
248ba4e596bSpooka 
2494b0f2948Spooka 	mntflags = pflags = 0;
2504b0f2948Spooka 	detach = 1;
25104c15978Spooka 	while ((ch = getopt(argc, argv, "o:rs")) != -1) {
252ba4e596bSpooka 		switch (ch) {
253ba4e596bSpooka 		case 'o':
254ba4e596bSpooka 			mp = getmntopts(optarg, puffsmopts, &mntflags, &pflags);
255ba4e596bSpooka 			if (mp == NULL)
256411f3e27Schristos 				err(EXIT_FAILURE, "getmntopts");
257ba4e596bSpooka 			freemntopts(mp);
258ba4e596bSpooka 			break;
25904c15978Spooka 		case 'r':
26004c15978Spooka 			rflag = 1;
26104c15978Spooka 			break;
262ba4e596bSpooka 		case 's':
2634b0f2948Spooka 			detach = 0;
264ba4e596bSpooka 			break;
265ba4e596bSpooka 		}
266ba4e596bSpooka 	}
267ba4e596bSpooka 	argv += optind;
268ba4e596bSpooka 	argc -= optind;
269ba4e596bSpooka 	pflags |= PUFFS_FLAG_BUILDPATH | PUFFS_KFLAG_NOCACHE;
270ba4e596bSpooka 
271ba4e596bSpooka 	if (pflags & PUFFS_FLAG_OPDUMP)
2724b0f2948Spooka 		detach = 0;
273ba4e596bSpooka 
274ba4e596bSpooka 	if (argc != 2)
275411f3e27Schristos 		usage();
276ba4e596bSpooka 
277ba4e596bSpooka 	PUFFSOP_INIT(pops);
278ba4e596bSpooka 
279ba4e596bSpooka 	PUFFSOP_SETFSNOP(pops, unmount);
280ba4e596bSpooka 	PUFFSOP_SETFSNOP(pops, sync);
281ba4e596bSpooka 	PUFFSOP_SETFSNOP(pops, statvfs);
282ba4e596bSpooka 	PUFFSOP_SET(pops, sysctlfs, fs, nodetofh);
283ba4e596bSpooka 	PUFFSOP_SET(pops, sysctlfs, fs, fhtonode);
284ba4e596bSpooka 
285ba4e596bSpooka 	PUFFSOP_SET(pops, sysctlfs, node, lookup);
286ba4e596bSpooka 	PUFFSOP_SET(pops, sysctlfs, node, getattr);
287ba4e596bSpooka 	PUFFSOP_SET(pops, sysctlfs, node, setattr);
288ba4e596bSpooka 	PUFFSOP_SET(pops, sysctlfs, node, readdir);
289ba4e596bSpooka 	PUFFSOP_SET(pops, sysctlfs, node, read);
290ba4e596bSpooka 	PUFFSOP_SET(pops, sysctlfs, node, write);
291ba4e596bSpooka 	PUFFSOP_SET(pops, puffs_genfs, node, reclaim);
292ba4e596bSpooka 
293ba4e596bSpooka 	pu = puffs_init(pops, _PATH_PUFFS, "sysctlfs", NULL, pflags);
294ba4e596bSpooka 	if (pu == NULL)
295411f3e27Schristos 		err(EXIT_FAILURE, "puffs_init");
296ba4e596bSpooka 
297ba4e596bSpooka 	puffs_set_pathbuild(pu, sysctlfs_pathbuild);
298ba4e596bSpooka 	puffs_set_pathtransform(pu, sysctlfs_pathtransform);
299ba4e596bSpooka 	puffs_set_pathcmp(pu, sysctlfs_pathcmp);
300ba4e596bSpooka 	puffs_set_pathfree(pu, sysctlfs_pathfree);
301ba4e596bSpooka 
302ba4e596bSpooka 	puffs_setfhsize(pu, sizeof(struct sfsfid), PUFFS_FHFLAG_NFSV3);
303ba4e596bSpooka 
304ba4e596bSpooka 	if (sysctlfs_domount(pu) != 0)
305411f3e27Schristos 		errx(EXIT_FAILURE, "domount");
306ba4e596bSpooka 
3074b0f2948Spooka 	if (detach)
3084462e945Spooka 		if (puffs_daemon(pu, 1, 1) == -1)
309411f3e27Schristos 			err(EXIT_FAILURE, "puffs_daemon");
3104b0f2948Spooka 
311c7528563Spooka #ifdef RUMP_ACTION
312c7528563Spooka 	{
313c7528563Spooka 		extern int puffs_fakecc;
314c7528563Spooka 		puffs_fakecc = 1;
315c7528563Spooka 		rump_init();
316c7528563Spooka 	}
317c7528563Spooka #endif
318c7528563Spooka 
319ec865a5bSpooka 	if (puffs_mount(pu, argv[1], mntflags, puffs_getroot(pu)) == -1)
320411f3e27Schristos 		err(EXIT_FAILURE, "puffs_mount");
3214b0f2948Spooka 	if (puffs_mainloop(pu) == -1)
322411f3e27Schristos 		err(EXIT_FAILURE, "mainloop");
323ba4e596bSpooka 
324ba4e596bSpooka 	return 0;
325ba4e596bSpooka }
326ba4e596bSpooka 
327ba4e596bSpooka static int
sysctlfs_domount(struct puffs_usermount * pu)328ba4e596bSpooka sysctlfs_domount(struct puffs_usermount *pu)
329ba4e596bSpooka {
330ba4e596bSpooka 	struct puffs_pathobj *po_root;
331ba4e596bSpooka 	struct puffs_node *pn_root;
332ba4e596bSpooka 	struct timeval tv_now;
333ba4e596bSpooka 
334ba4e596bSpooka 	rn.myid = 2;
335ba4e596bSpooka 	rn.sysctl_flags = CTLTYPE_NODE;
336ba4e596bSpooka 
337ba4e596bSpooka 	gettimeofday(&tv_now, NULL);
338ba4e596bSpooka 	TIMEVAL_TO_TIMESPEC(&tv_now, &fstime);
339ba4e596bSpooka 
340ba4e596bSpooka 	pn_root = puffs_pn_new(pu, &rn);
341ba4e596bSpooka 	assert(pn_root != NULL);
342ba4e596bSpooka 	puffs_setroot(pu, pn_root);
343ba4e596bSpooka 
344ba4e596bSpooka 	po_root = puffs_getrootpathobj(pu);
345ba4e596bSpooka 	po_root->po_path = &sname_root;
346ba4e596bSpooka 	po_root->po_len = 0;
347ba4e596bSpooka 
34862234858Spooka 	fileuid = geteuid();
34962234858Spooka 	filegid = getegid();
35062234858Spooka 
35162234858Spooka 	if (fileuid == 0)
352b8d16c7dSchristos 		fileperms = 0644;
35362234858Spooka 	else
354b8d16c7dSchristos 		fileperms = 0444;
35562234858Spooka 
356ba4e596bSpooka 	return 0;
357ba4e596bSpooka }
358ba4e596bSpooka 
359ba4e596bSpooka int
sysctlfs_fs_fhtonode(struct puffs_usermount * pu,void * fid,size_t fidsize,struct puffs_newinfo * pni)36021913eabSpooka sysctlfs_fs_fhtonode(struct puffs_usermount *pu, void *fid, size_t fidsize,
361ba4e596bSpooka 	struct puffs_newinfo *pni)
362ba4e596bSpooka {
363ba4e596bSpooka 	struct puffs_pathobj po;
364ba4e596bSpooka 	struct puffs_node *pn;
365ba4e596bSpooka 	struct sfsnode *sfs;
366ba4e596bSpooka 	struct sfsfid *sfid;
367ba4e596bSpooka 
368ba4e596bSpooka 	sfid = fid;
369ba4e596bSpooka 
370ba4e596bSpooka 	po.po_len = sfid->len;
371ba4e596bSpooka 	po.po_path = &sfid->path;
372ba4e596bSpooka 
37321913eabSpooka 	pn = getnode(pu, &po, 0);
374ba4e596bSpooka 	if (pn == NULL)
375ba4e596bSpooka 		return EINVAL;
376ba4e596bSpooka 	sfs = pn->pn_data;
377ba4e596bSpooka 
378ba4e596bSpooka 	puffs_newinfo_setcookie(pni, pn);
379ba4e596bSpooka 	if (ISADIR(sfs))
380ba4e596bSpooka 		puffs_newinfo_setvtype(pni, VDIR);
381ba4e596bSpooka 	else
382ba4e596bSpooka 		puffs_newinfo_setvtype(pni, VREG);
383ba4e596bSpooka 
384ba4e596bSpooka 	return 0;
385ba4e596bSpooka }
386ba4e596bSpooka 
387ba4e596bSpooka int
sysctlfs_fs_nodetofh(struct puffs_usermount * pu,void * cookie,void * fid,size_t * fidsize)38821913eabSpooka sysctlfs_fs_nodetofh(struct puffs_usermount *pu, void *cookie,
389ba4e596bSpooka 	void *fid, size_t *fidsize)
390ba4e596bSpooka {
391ba4e596bSpooka 	struct puffs_node *pn = cookie;
392ba4e596bSpooka 	struct sfsfid *sfid;
393ba4e596bSpooka 
394ba4e596bSpooka 	sfid = fid;
395ba4e596bSpooka 	sfid->len = PNPLEN(pn);
396ba4e596bSpooka 	memcpy(&sfid->path, PNPATH(pn), sfid->len * sizeof(int));
397ba4e596bSpooka 
398ba4e596bSpooka 	return 0;
399ba4e596bSpooka }
400ba4e596bSpooka 
401ba4e596bSpooka static void
getnodedata(struct sfsnode * sfs,struct puffs_pathobj * po,char * buf,size_t * bufsize)40204c15978Spooka getnodedata(struct sfsnode *sfs, struct puffs_pathobj *po,
40304c15978Spooka 	char *buf, size_t *bufsize)
404ba4e596bSpooka {
405ba4e596bSpooka 	size_t sz;
40604c15978Spooka 	int error = 0;
407ba4e596bSpooka 
408ba4e596bSpooka 	assert(!ISADIR(sfs));
409ba4e596bSpooka 
41004c15978Spooka 	memset(buf, 0, *bufsize);
411ba4e596bSpooka 	switch (SYSCTL_TYPE(sfs->sysctl_flags)) {
4120a80f657Spooka 	case CTLTYPE_BOOL: {
4130a80f657Spooka 		bool b;
4140a80f657Spooka 		sz = sizeof(bool);
4150a80f657Spooka 		assert(sz <= *bufsize);
4160a80f657Spooka 		if (sysctl(po->po_path, po->po_len, &b, &sz, NULL, 0) == -1) {
4170a80f657Spooka 			error = errno;
4180a80f657Spooka 			break;
4190a80f657Spooka 		}
4200a80f657Spooka 		if (rflag)
4210a80f657Spooka 			memcpy(buf, &b, sz);
4220a80f657Spooka 		else
4230a80f657Spooka 			snprintf(buf, *bufsize, "%s", b ? "true" : "false");
4240a80f657Spooka 		break;
4250a80f657Spooka 	}
426ba4e596bSpooka 	case CTLTYPE_INT: {
427ba4e596bSpooka 		int i;
428ba4e596bSpooka 		sz = sizeof(int);
42904c15978Spooka 		assert(sz <= *bufsize);
43004c15978Spooka 		if (sysctl(po->po_path, po->po_len, &i, &sz, NULL, 0) == -1) {
43104c15978Spooka 			error = errno;
432ba4e596bSpooka 			break;
43304c15978Spooka 		}
43404c15978Spooka 		if (rflag)
43504c15978Spooka 			memcpy(buf, &i, sz);
43604c15978Spooka 		else
43704c15978Spooka 			snprintf(buf, *bufsize, "%d", i);
438ba4e596bSpooka 		break;
439ba4e596bSpooka 	}
440ba4e596bSpooka 	case CTLTYPE_QUAD: {
441ba4e596bSpooka 		quad_t q;
442ba4e596bSpooka 		sz = sizeof(q);
44304c15978Spooka 		assert(sz <= *bufsize);
44404c15978Spooka 		if (sysctl(po->po_path, po->po_len, &q, &sz, NULL, 0) == -1) {
44504c15978Spooka 			error = errno;
446ba4e596bSpooka 			break;
44704c15978Spooka 		}
44804c15978Spooka 		if (rflag)
44904c15978Spooka 			memcpy(buf, &q, sz);
45004c15978Spooka 		else
45104c15978Spooka 			snprintf(buf, *bufsize, "%" PRId64, q);
452ba4e596bSpooka 		break;
453ba4e596bSpooka 	}
454c8360f5cSpooka 	case CTLTYPE_STRUCT: {
455c8360f5cSpooka 		uint8_t snode[SFS_MAXFILE/2-1];
456c8360f5cSpooka 		unsigned i;
457c8360f5cSpooka 
458c8360f5cSpooka 		sz = sizeof(snode);
45904c15978Spooka 		assert(sz <= *bufsize);
46004c15978Spooka 		if (sysctl(po->po_path, po->po_len, snode, &sz, NULL, 0) == -1){
46104c15978Spooka 			error = errno;
462ba4e596bSpooka 			break;
46304c15978Spooka 		}
46404c15978Spooka 		if (rflag) {
46504c15978Spooka 			memcpy(buf, &snode, sz);
46604c15978Spooka 		} else {
46704c15978Spooka 			for (i = 0; i < sz && 2*i < *bufsize; i++) {
468c8360f5cSpooka 				sprintf(&buf[2*i], "%02x", snode[i]);
469c8360f5cSpooka 			}
470c8360f5cSpooka 			buf[2*i] = '\0';
47104c15978Spooka 		}
472c8360f5cSpooka 		break;
473c8360f5cSpooka 	}
474ba4e596bSpooka 	case CTLTYPE_STRING: {
47504c15978Spooka 		sz = *bufsize;
47604c15978Spooka 		assert(sz <= *bufsize);
47704c15978Spooka 		if (sysctl(po->po_path, po->po_len, buf, &sz, NULL, 0) == -1) {
47804c15978Spooka 			error = errno;
479ba4e596bSpooka 			break;
48004c15978Spooka 		}
481ba4e596bSpooka 		break;
482ba4e596bSpooka 	}
483ba4e596bSpooka 	default:
4840a80f657Spooka 		snprintf(buf, *bufsize, "invalid sysctl CTLTYPE %d",
4850a80f657Spooka 		    SYSCTL_TYPE(sfs->sysctl_flags));
486ba4e596bSpooka 		break;
487ba4e596bSpooka 	}
48804c15978Spooka 
48904c15978Spooka 	if (error) {
49004c15978Spooka 		*bufsize = 0;
49104c15978Spooka 		return;
49204c15978Spooka 	}
49304c15978Spooka 
49404c15978Spooka 	if (rflag)
49504c15978Spooka 		*bufsize = sz;
49604c15978Spooka 	else
49704c15978Spooka 		*bufsize = strlen(buf);
498ba4e596bSpooka }
499ba4e596bSpooka 
500ba4e596bSpooka static int
getlinks(struct sfsnode * sfs,struct puffs_pathobj * po)501ba4e596bSpooka getlinks(struct sfsnode *sfs, struct puffs_pathobj *po)
502ba4e596bSpooka {
503ba4e596bSpooka 	struct sysctlnode sn[SFS_NODEPERDIR];
504ba4e596bSpooka 	struct sysctlnode qnode;
505ba4e596bSpooka 	SfsName *sname;
506ba4e596bSpooka 	size_t sl;
507ba4e596bSpooka 
508ba4e596bSpooka 	if (!ISADIR(sfs))
509ba4e596bSpooka 		return 1;
510ba4e596bSpooka 
511ba4e596bSpooka 	memset(&qnode, 0, sizeof(qnode));
512ba4e596bSpooka 	sl = sizeof(sn);
513ba4e596bSpooka 	qnode.sysctl_flags = SYSCTL_VERSION;
514ba4e596bSpooka 	sname = po->po_path;
515ba4e596bSpooka 	(*sname)[po->po_len] = CTL_QUERY;
516ba4e596bSpooka 
517ba4e596bSpooka 	if (sysctl(*sname, po->po_len + 1, sn, &sl,
518ba4e596bSpooka 	    &qnode, sizeof(qnode)) == -1)
519ba4e596bSpooka 		return 0;
520ba4e596bSpooka 
521ba4e596bSpooka 	return (sl / sizeof(sn[0])) + 2;
522ba4e596bSpooka }
523ba4e596bSpooka 
524ba4e596bSpooka static int
getsize(struct sfsnode * sfs,struct puffs_pathobj * po)525ba4e596bSpooka getsize(struct sfsnode *sfs, struct puffs_pathobj *po)
526ba4e596bSpooka {
527ba4e596bSpooka 	char buf[SFS_MAXFILE];
52804c15978Spooka 	size_t sz = sizeof(buf);
529ba4e596bSpooka 
530ba4e596bSpooka 	if (ISADIR(sfs))
531ba4e596bSpooka 		return getlinks(sfs, po) * 16; /* totally arbitrary */
532ba4e596bSpooka 
53304c15978Spooka 	getnodedata(sfs, po, buf, &sz);
53404c15978Spooka 	if (rflag)
53504c15978Spooka 		return sz;
53604c15978Spooka 	else
53704c15978Spooka 		return sz + 1; /* for \n, not \0 */
538ba4e596bSpooka }
539ba4e596bSpooka 
540ba4e596bSpooka int
sysctlfs_node_lookup(struct puffs_usermount * pu,void * opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn)54121913eabSpooka sysctlfs_node_lookup(struct puffs_usermount *pu, void *opc,
54221913eabSpooka 	struct puffs_newinfo *pni, const struct puffs_cn *pcn)
543ba4e596bSpooka {
544ba4e596bSpooka 	struct puffs_cn *p2cn = __UNCONST(pcn); /* XXX: fix the interface */
545ba4e596bSpooka 	struct sysctlnode sn[SFS_NODEPERDIR];
546ba4e596bSpooka 	struct sysctlnode qnode;
547ba4e596bSpooka 	struct puffs_node *pn_dir = opc;
548ba4e596bSpooka 	struct puffs_node *pn_new;
549ba4e596bSpooka 	struct sfsnode *sfs_dir = pn_dir->pn_data, *sfs_new;
550ba4e596bSpooka 	SfsName *sname = PCNPATH(pcn);
5513cafe960Slukem 	size_t sl, i;
5523cafe960Slukem 	int nodetype;
553ba4e596bSpooka 
554ba4e596bSpooka 	assert(ISADIR(sfs_dir));
555ba4e596bSpooka 
556ba4e596bSpooka 	/*
557ba4e596bSpooka 	 * If we're looking for dotdot, we already have the entire pathname
558ba4e596bSpooka 	 * in sname, courtesy of pathbuild, so we can skip this step.
559ba4e596bSpooka 	 */
560ba4e596bSpooka 	if (!PCNISDOTDOT(pcn)) {
561ba4e596bSpooka 		memset(&qnode, 0, sizeof(qnode));
562ba4e596bSpooka 		sl = SFS_NODEPERDIR * sizeof(struct sysctlnode);
563ba4e596bSpooka 		qnode.sysctl_flags = SYSCTL_VERSION;
564ba4e596bSpooka 		(*sname)[PCNPLEN(pcn)] = CTL_QUERY;
565ba4e596bSpooka 
566ba4e596bSpooka 		if (sysctl(*sname, PCNPLEN(pcn) + 1, sn, &sl,
567ba4e596bSpooka 		    &qnode, sizeof(qnode)) == -1)
568ba4e596bSpooka 			return ENOENT;
569ba4e596bSpooka 
570ba4e596bSpooka 		for (i = 0; i < sl / sizeof(struct sysctlnode); i++)
571ba4e596bSpooka 			if (strcmp(sn[i].sysctl_name, pcn->pcn_name) == 0)
572ba4e596bSpooka 				break;
573ba4e596bSpooka 		if (i == sl / sizeof(struct sysctlnode))
574ba4e596bSpooka 			return ENOENT;
575ba4e596bSpooka 
576ba4e596bSpooka 		(*sname)[PCNPLEN(pcn)] = sn[i].sysctl_num;
577ba4e596bSpooka 		p2cn->pcn_po_full.po_len++;
578ba4e596bSpooka 		nodetype = sn[i].sysctl_flags;
579ba4e596bSpooka 	} else
580ba4e596bSpooka 		nodetype = CTLTYPE_NODE;
581ba4e596bSpooka 
582ba4e596bSpooka 	pn_new = getnode(pu, &p2cn->pcn_po_full, nodetype);
58353500167Schristos 	if (pn_new == NULL)
58453500167Schristos 		return ENOENT;
585ba4e596bSpooka 	sfs_new = pn_new->pn_data;
586ba4e596bSpooka 
587ba4e596bSpooka 	puffs_newinfo_setcookie(pni, pn_new);
588ba4e596bSpooka 	if (ISADIR(sfs_new))
589ba4e596bSpooka 		puffs_newinfo_setvtype(pni, VDIR);
590ba4e596bSpooka 	else
591ba4e596bSpooka 		puffs_newinfo_setvtype(pni, VREG);
592ba4e596bSpooka 
593ba4e596bSpooka 	return 0;
594ba4e596bSpooka }
595ba4e596bSpooka 
596ba4e596bSpooka int
sysctlfs_node_getattr(struct puffs_usermount * pu,void * opc,struct vattr * va,const struct puffs_cred * pcr)59721913eabSpooka sysctlfs_node_getattr(struct puffs_usermount *pu, void *opc, struct vattr *va,
5986a3d9a18Spooka 	const struct puffs_cred *pcr)
599ba4e596bSpooka {
600ba4e596bSpooka 	struct puffs_node *pn = opc;
601ba4e596bSpooka 	struct sfsnode *sfs = pn->pn_data;
602ba4e596bSpooka 
603ba4e596bSpooka 	memset(va, 0, sizeof(struct vattr));
604ba4e596bSpooka 
605ba4e596bSpooka 	if (ISADIR(sfs)) {
606ba4e596bSpooka 		va->va_type = VDIR;
60762234858Spooka 		va->va_mode = 0555;
608ba4e596bSpooka 	} else {
609ba4e596bSpooka 		va->va_type = VREG;
61062234858Spooka 		va->va_mode = fileperms;
611ba4e596bSpooka 	}
61262234858Spooka 	va->va_uid = fileuid;
61362234858Spooka 	va->va_gid = filegid;
614ba4e596bSpooka 	va->va_nlink = getlinks(sfs, &pn->pn_po);
615ba4e596bSpooka 	va->va_fileid = sfs->myid;
616ba4e596bSpooka 	va->va_size = getsize(sfs, &pn->pn_po);
617ba4e596bSpooka 	va->va_gen = 1;
618ba4e596bSpooka 	va->va_rdev = PUFFS_VNOVAL;
619ba4e596bSpooka 	va->va_blocksize = 512;
620ba4e596bSpooka 	va->va_filerev = 1;
621ba4e596bSpooka 
622ba4e596bSpooka 	va->va_atime = va->va_mtime = va->va_ctime = va->va_birthtime = fstime;
623ba4e596bSpooka 
624ba4e596bSpooka 	return 0;
625ba4e596bSpooka }
626ba4e596bSpooka 
627ba4e596bSpooka int
sysctlfs_node_setattr(struct puffs_usermount * pu,void * opc,const struct vattr * va,const struct puffs_cred * pcr)62821913eabSpooka sysctlfs_node_setattr(struct puffs_usermount *pu, void *opc,
6296a3d9a18Spooka 	const struct vattr *va, const struct puffs_cred *pcr)
630ba4e596bSpooka {
631ba4e596bSpooka 
632ba4e596bSpooka 	/* dummy, but required for write */
63362234858Spooka 	/* XXX: we could return EOPNOTSUPP or something */
634ba4e596bSpooka 	return 0;
635ba4e596bSpooka }
636ba4e596bSpooka 
637ba4e596bSpooka int
sysctlfs_node_readdir(struct puffs_usermount * pu,void * opc,struct dirent * dent,off_t * readoff,size_t * reslen,const struct puffs_cred * pcr,int * eofflag,off_t * cookies,size_t * ncookies)63821913eabSpooka sysctlfs_node_readdir(struct puffs_usermount *pu, void *opc,
63921913eabSpooka 	struct dirent *dent, off_t *readoff, size_t *reslen,
64021913eabSpooka 	const struct puffs_cred *pcr, int *eofflag,
64121913eabSpooka 	off_t *cookies, size_t *ncookies)
642ba4e596bSpooka {
643ba4e596bSpooka 	struct sysctlnode sn[SFS_NODEPERDIR];
644ba4e596bSpooka 	struct sysctlnode qnode;
645ba4e596bSpooka 	struct puffs_node *pn_dir = opc;
646ba4e596bSpooka 	struct puffs_node *pn_res;
647ba4e596bSpooka 	struct puffs_pathobj po;
648ba4e596bSpooka 	struct sfsnode *sfs_dir = pn_dir->pn_data, *sfs_ent;
649ba4e596bSpooka 	SfsName *sname;
6503cafe960Slukem 	size_t sl, i;
651ba4e596bSpooka 	enum vtype vt;
652ba4e596bSpooka 	ino_t id;
653ba4e596bSpooka 
654ba4e596bSpooka 	*ncookies = 0;
655ba4e596bSpooka 
656ba4e596bSpooka  again:
657ba4e596bSpooka 	if (*readoff == DENT_DOT || *readoff == DENT_DOTDOT) {
658ba4e596bSpooka 		puffs_gendotdent(&dent, sfs_dir->myid, *readoff, reslen);
659ba4e596bSpooka 		(*readoff)++;
660ba4e596bSpooka 		PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff);
661ba4e596bSpooka 		goto again;
662ba4e596bSpooka 	}
663ba4e596bSpooka 
664ba4e596bSpooka 	memset(&qnode, 0, sizeof(qnode));
665ba4e596bSpooka 	sl = SFS_NODEPERDIR * sizeof(struct sysctlnode);
666ba4e596bSpooka 	qnode.sysctl_flags = SYSCTL_VERSION;
667ba4e596bSpooka 	sname = PNPATH(pn_dir);
668ba4e596bSpooka 	(*sname)[PNPLEN(pn_dir)] = CTL_QUERY;
669ba4e596bSpooka 
670ba4e596bSpooka 	if (sysctl(*sname, PNPLEN(pn_dir) + 1, sn, &sl,
671ba4e596bSpooka 	    &qnode, sizeof(qnode)) == -1)
672ba4e596bSpooka 		return ENOENT;
673ba4e596bSpooka 
674ba4e596bSpooka 	po.po_path = sname;
675ba4e596bSpooka 	po.po_len = PNPLEN(pn_dir)+1;
676ba4e596bSpooka 
677ba4e596bSpooka 	for (i = DENT_ADJ(*readoff); i < sl / sizeof(struct sysctlnode); i++) {
678ba4e596bSpooka 		if (SYSCTL_TYPE(sn[i].sysctl_flags) == CTLTYPE_NODE)
679ba4e596bSpooka 			vt = VDIR;
680ba4e596bSpooka 		else
681ba4e596bSpooka 			vt = VREG;
682ba4e596bSpooka 
683ba4e596bSpooka 		/*
684ba4e596bSpooka 		 * check if the node exists.  if so, give it the real
685ba4e596bSpooka 		 * inode number.  otherwise just fake it.
686ba4e596bSpooka 		 */
687ba4e596bSpooka 		(*sname)[PNPLEN(pn_dir)] = sn[i].sysctl_num;
688ba4e596bSpooka 		pn_res = puffs_pn_nodewalk(pu, puffs_path_walkcmp, &po);
689ba4e596bSpooka 		if (pn_res) {
690ba4e596bSpooka 			sfs_ent = pn_res->pn_data;
691ba4e596bSpooka 			id = sfs_ent->myid;
692ba4e596bSpooka 		} else {
693ba4e596bSpooka 			id = nextid++;
694ba4e596bSpooka 		}
695ba4e596bSpooka 
696ba4e596bSpooka 		if (!puffs_nextdent(&dent, sn[i].sysctl_name, id,
697ba4e596bSpooka 		    puffs_vtype2dt(vt), reslen))
698ba4e596bSpooka 			return 0;
699ba4e596bSpooka 
700ba4e596bSpooka 		(*readoff)++;
701ba4e596bSpooka 		PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff);
702ba4e596bSpooka 	}
703ba4e596bSpooka 
704ba4e596bSpooka 	*eofflag = 1;
705ba4e596bSpooka 	return 0;
706ba4e596bSpooka }
707ba4e596bSpooka 
708ba4e596bSpooka int
sysctlfs_node_read(struct puffs_usermount * pu,void * opc,uint8_t * buf,off_t offset,size_t * resid,const struct puffs_cred * pcr,int ioflag)70921913eabSpooka sysctlfs_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf,
710ba4e596bSpooka 	off_t offset, size_t *resid, const struct puffs_cred *pcr,
711ba4e596bSpooka 	int ioflag)
712ba4e596bSpooka {
713ba4e596bSpooka 	char localbuf[SFS_MAXFILE];
714ba4e596bSpooka 	struct puffs_node *pn = opc;
715ba4e596bSpooka 	struct sfsnode *sfs = pn->pn_data;
71604c15978Spooka 	size_t sz = sizeof(localbuf);
717ba4e596bSpooka 	int xfer;
718ba4e596bSpooka 
719ba4e596bSpooka 	if (ISADIR(sfs))
720ba4e596bSpooka 		return EISDIR;
721ba4e596bSpooka 
72204c15978Spooka 	getnodedata(sfs, &pn->pn_po, localbuf, &sz);
72304c15978Spooka 	if ((ssize_t)sz < offset)
7244c9ac951Snjoly 		xfer = 0;
7254c9ac951Snjoly 	else
72604c15978Spooka 		xfer = MIN(*resid, sz - offset);
727ba4e596bSpooka 
728ba4e596bSpooka 	if (xfer <= 0)
729ba4e596bSpooka 		return 0;
730ba4e596bSpooka 
731ba4e596bSpooka 	memcpy(buf, localbuf + offset, xfer);
732ba4e596bSpooka 	*resid -= xfer;
733ba4e596bSpooka 
73404c15978Spooka 	if (*resid && !rflag) {
735ba4e596bSpooka 		buf[xfer] = '\n';
736ba4e596bSpooka 		(*resid)--;
737ba4e596bSpooka 	}
738ba4e596bSpooka 
739ba4e596bSpooka 	return 0;
740ba4e596bSpooka }
741ba4e596bSpooka 
742ba4e596bSpooka int
sysctlfs_node_write(struct puffs_usermount * pu,void * opc,uint8_t * buf,off_t offset,size_t * resid,const struct puffs_cred * cred,int ioflag)74321913eabSpooka sysctlfs_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf,
744ba4e596bSpooka 	off_t offset, size_t *resid, const struct puffs_cred *cred,
745ba4e596bSpooka 	int ioflag)
746ba4e596bSpooka {
747ba4e596bSpooka 	struct puffs_node *pn = opc;
748ba4e596bSpooka 	struct sfsnode *sfs = pn->pn_data;
749ba4e596bSpooka 	long long ll;
750ba4e596bSpooka 	int i, rv;
7510a80f657Spooka 	bool b;
752ba4e596bSpooka 
75304c15978Spooka 	/*
75404c15978Spooka 	 * I picked the wrong day to ... um, the wrong place to return errors
75504c15978Spooka 	 */
75604c15978Spooka 
75704c15978Spooka 	/* easy to support, but just unavailable now */
75804c15978Spooka 	if (rflag)
75904c15978Spooka 		return EOPNOTSUPP;
76004c15978Spooka 
761ba4e596bSpooka 	if (puffs_cred_isjuggernaut(cred) == 0)
762ba4e596bSpooka 		return EACCES;
763ba4e596bSpooka 
764ba4e596bSpooka 	if (ISADIR(sfs))
765ba4e596bSpooka 		return EISDIR;
766ba4e596bSpooka 
767ba4e596bSpooka 	if (offset != 0)
768ba4e596bSpooka 		return EINVAL;
769ba4e596bSpooka 
770ba4e596bSpooka 	if (ioflag & PUFFS_IO_APPEND)
771ba4e596bSpooka 		return EINVAL;
772ba4e596bSpooka 
773ba4e596bSpooka 	switch (SYSCTL_TYPE(sfs->sysctl_flags)) {
7740a80f657Spooka 	case CTLTYPE_BOOL:
7750a80f657Spooka 		if (strcasestr((const char *)buf, "true"))
7760a80f657Spooka 			b = true;
7770a80f657Spooka 		else if (strcasestr((const char *)buf, "false"))
7780a80f657Spooka 			b = false;
7790a80f657Spooka 		else
7800a80f657Spooka 			return EINVAL;
7810a80f657Spooka 		rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL,
7820a80f657Spooka 		    &b, sizeof(b));
7830a80f657Spooka 		break;
784ba4e596bSpooka 	case CTLTYPE_INT:
785ba4e596bSpooka 		if (sscanf((const char *)buf, "%d", &i) != 1)
786ba4e596bSpooka 			return EINVAL;
787ba4e596bSpooka 		rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL,
788ba4e596bSpooka 		    &i, sizeof(int));
789ba4e596bSpooka 		break;
790ba4e596bSpooka 	case CTLTYPE_QUAD:
791ba4e596bSpooka 		if (sscanf((const char *)buf, "%lld", &ll) != 1)
792ba4e596bSpooka 			return EINVAL;
793ba4e596bSpooka 		rv =  sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL,
794ba4e596bSpooka 		    &ll, sizeof(long long));
795ba4e596bSpooka 		break;
796ba4e596bSpooka 	case CTLTYPE_STRING:
797ba4e596bSpooka 		rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL, buf, *resid);
798ba4e596bSpooka 		break;
799ba4e596bSpooka 	default:
800ba4e596bSpooka 		rv = EINVAL;
801ba4e596bSpooka 		break;
802ba4e596bSpooka 	}
803ba4e596bSpooka 
804ba4e596bSpooka 	if (rv)
805ba4e596bSpooka 		return rv;
806ba4e596bSpooka 
807ba4e596bSpooka 	*resid = 0;
808ba4e596bSpooka 	return 0;
809ba4e596bSpooka }
810