Home | History | Annotate | Line # | Download | only in mount_sysctlfs
sysctlfs.c revision 1.18
      1 /*	$NetBSD: sysctlfs.c,v 1.18 2015/11/12 16:51:18 christos Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2006, 2007  Antti Kantee.  All Rights Reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
     16  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     18  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     25  * SUCH DAMAGE.
     26  */
     27 
     28 /*
     29  * sysctlfs: mount sysctls as a file system tree.  Supports query and
     30  * modify of nodes in the sysctl namespace in addition to namespace
     31  * traversal.
     32  */
     33 
     34 #include <sys/cdefs.h>
     35 #ifndef lint
     36 __RCSID("$NetBSD: sysctlfs.c,v 1.18 2015/11/12 16:51:18 christos Exp $");
     37 #endif /* !lint */
     38 
     39 #include <sys/types.h>
     40 #include <sys/sysctl.h>
     41 
     42 #include <stdio.h>
     43 #include <assert.h>
     44 #include <err.h>
     45 #include <errno.h>
     46 #include <mntopts.h>
     47 #include <paths.h>
     48 #include <puffs.h>
     49 #include <stdlib.h>
     50 #include <string.h>
     51 #include <unistd.h>
     52 #include <util.h>
     53 
     54 #ifdef RUMP_ACTION
     55 #include <rump/rump.h>
     56 #include <rump/rump_syscalls.h>
     57 
     58 #define sysctl(a,b,c,d,e,f) rump_sys___sysctl(a,b,c,d,e,f)
     59 #endif
     60 
     61 PUFFSOP_PROTOS(sysctlfs)
     62 
     63 struct sfsnode {
     64 	int sysctl_flags;
     65 	ino_t myid;
     66 };
     67 
     68 #define SFSPATH_DOTDOT 0
     69 #define SFSPATH_NORMAL 1
     70 
     71 #define N_HIERARCHY 10
     72 typedef int SfsName[N_HIERARCHY];
     73 
     74 struct sfsfid {
     75 	int len;
     76 	SfsName path;
     77 };
     78 
     79 static struct sfsnode rn;
     80 static SfsName sname_root;
     81 static struct timespec fstime;
     82 
     83 static ino_t nextid = 3;
     84 static mode_t fileperms;
     85 static uid_t fileuid;
     86 static gid_t filegid;
     87 
     88 static int rflag;
     89 
     90 #define ISADIR(a) ((SYSCTL_TYPE(a->sysctl_flags) == CTLTYPE_NODE))
     91 #define SFS_MAXFILE 32768
     92 #define SFS_NODEPERDIR 128
     93 
     94 static int sysctlfs_domount(struct puffs_usermount *);
     95 
     96 /*
     97  * build paths.  doesn't support rename (but neither does the fs)
     98  */
     99 static int
    100 sysctlfs_pathbuild(struct puffs_usermount *pu,
    101     const struct puffs_pathobj *parent, const struct puffs_pathobj *comp,
    102     size_t offset, struct puffs_pathobj *res)
    103 {
    104 	SfsName *sname;
    105 	size_t clen;
    106 
    107 	assert(parent->po_len < N_HIERARCHY); /* code uses +1 */
    108 
    109 	sname = malloc(sizeof(SfsName));
    110 	assert(sname != NULL);
    111 
    112 	clen = parent->po_len;
    113 	if (comp->po_len == SFSPATH_DOTDOT) {
    114 		assert(clen != 0);
    115 		clen--;
    116 	}
    117 
    118 	memcpy(sname, parent->po_path, clen * sizeof(int));
    119 
    120 	res->po_path = sname;
    121 	res->po_len = clen;
    122 
    123 	return 0;
    124 }
    125 
    126 static int
    127 sysctlfs_pathtransform(struct puffs_usermount *pu,
    128     const struct puffs_pathobj *p, const struct puffs_cn *pcn,
    129     struct puffs_pathobj *res)
    130 {
    131 
    132 	res->po_path = NULL;
    133 	/*
    134 	 * XXX: overload.  prevents us from doing rename, but the fs
    135 	 * (and sysctl(3)) doesn't support it, so no biggie
    136 	 */
    137 	if (PCNISDOTDOT(pcn)) {
    138 		res->po_len = SFSPATH_DOTDOT;
    139 	}else {
    140 		res->po_len = SFSPATH_NORMAL;
    141 	}
    142 
    143 	return 0;
    144 }
    145 
    146 static int
    147 sysctlfs_pathcmp(struct puffs_usermount *pu, struct puffs_pathobj *po1,
    148     struct puffs_pathobj *po2, size_t clen, int checkprefix)
    149 {
    150 
    151 	if (memcmp(po1->po_path, po2->po_path, clen * sizeof(int)) == 0)
    152 		return 0;
    153 	return 1;
    154 }
    155 
    156 static void
    157 sysctlfs_pathfree(struct puffs_usermount *pu, struct puffs_pathobj *po)
    158 {
    159 
    160 	free(po->po_path);
    161 }
    162 
    163 static struct puffs_node *
    164 getnode(struct puffs_usermount *pu, struct puffs_pathobj *po, int nodetype)
    165 {
    166 	struct sysctlnode sn[SFS_NODEPERDIR];
    167 	struct sysctlnode qnode;
    168 	struct puffs_node *pn;
    169 	struct sfsnode *sfs;
    170 	SfsName myname, *sname;
    171 	size_t sl, i;
    172 
    173 	/*
    174 	 * Check if we need to create a new in-memory node or if we
    175 	 * already have one for this path.  Shortcut for the rootnode.
    176 	 * Also, memcmp against zero-length would be quite true always.
    177 	 */
    178 	if (po->po_len == 0)
    179 		pn = puffs_getroot(pu);
    180 	else
    181 		pn = puffs_pn_nodewalk(pu, puffs_path_walkcmp, po);
    182 
    183 	if (pn == NULL)
    184 		return NULL;
    185 	/*
    186 	 * don't know nodetype?  query...
    187 	 *
    188 	 * XXX1: nothing really guarantees 0 is an invalid nodetype
    189 	 * XXX2: is there really no easier way of doing this?  we
    190 	 *       know the whole mib path
    191 	 */
    192 	if (!nodetype) {
    193 		sname = po->po_path;
    194 		memcpy(myname, po->po_path, po->po_len * sizeof(myname[0]));
    195 
    196 		memset(&qnode, 0, sizeof(qnode));
    197 		qnode.sysctl_flags = SYSCTL_VERSION;
    198 		myname[po->po_len-1] = CTL_QUERY;
    199 
    200 		sl = sizeof(sn);
    201 		if (sysctl(myname, po->po_len, sn, &sl,
    202 		    &qnode, sizeof(qnode)) == -1)
    203 			abort();
    204 
    205 		for (i = 0; i < sl / sizeof(struct sysctlnode); i++) {
    206 			 if (sn[i].sysctl_num == (*sname)[po->po_len-1]) {
    207 				nodetype = sn[i].sysctl_flags;
    208 				break;
    209 			}
    210 		}
    211 		if (!nodetype)
    212 			return NULL;
    213 	}
    214 
    215 	sfs = emalloc(sizeof(*sfs));
    216 	sfs->sysctl_flags = nodetype;
    217 	sfs->myid = nextid++;
    218 
    219 	pn = puffs_pn_new(pu, sfs);
    220 	assert(pn);
    221 
    222 	return pn;
    223 }
    224 
    225 static void __dead
    226 usage(void)
    227 {
    228 
    229 	fprintf(stderr, "Usage: %s [-o <mntopts>] sysctlfs mountpath",
    230 	    getprogname());
    231 	exit(1);
    232 }
    233 
    234 int
    235 main(int argc, char *argv[])
    236 {
    237 	struct puffs_usermount *pu;
    238 	struct puffs_ops *pops;
    239 	mntoptparse_t mp;
    240 	int mntflags, pflags;
    241 	int detach;
    242 	int ch;
    243 
    244 	setprogname(argv[0]);
    245 
    246 	if (argc < 2)
    247 		usage();
    248 
    249 	mntflags = pflags = 0;
    250 	detach = 1;
    251 	while ((ch = getopt(argc, argv, "o:rs")) != -1) {
    252 		switch (ch) {
    253 		case 'o':
    254 			mp = getmntopts(optarg, puffsmopts, &mntflags, &pflags);
    255 			if (mp == NULL)
    256 				err(EXIT_FAILURE, "getmntopts");
    257 			freemntopts(mp);
    258 			break;
    259 		case 'r':
    260 			rflag = 1;
    261 			break;
    262 		case 's':
    263 			detach = 0;
    264 			break;
    265 		}
    266 	}
    267 	argv += optind;
    268 	argc -= optind;
    269 	pflags |= PUFFS_FLAG_BUILDPATH | PUFFS_KFLAG_NOCACHE;
    270 
    271 	if (pflags & PUFFS_FLAG_OPDUMP)
    272 		detach = 0;
    273 
    274 	if (argc != 2)
    275 		usage();
    276 
    277 	PUFFSOP_INIT(pops);
    278 
    279 	PUFFSOP_SETFSNOP(pops, unmount);
    280 	PUFFSOP_SETFSNOP(pops, sync);
    281 	PUFFSOP_SETFSNOP(pops, statvfs);
    282 	PUFFSOP_SET(pops, sysctlfs, fs, nodetofh);
    283 	PUFFSOP_SET(pops, sysctlfs, fs, fhtonode);
    284 
    285 	PUFFSOP_SET(pops, sysctlfs, node, lookup);
    286 	PUFFSOP_SET(pops, sysctlfs, node, getattr);
    287 	PUFFSOP_SET(pops, sysctlfs, node, setattr);
    288 	PUFFSOP_SET(pops, sysctlfs, node, readdir);
    289 	PUFFSOP_SET(pops, sysctlfs, node, read);
    290 	PUFFSOP_SET(pops, sysctlfs, node, write);
    291 	PUFFSOP_SET(pops, puffs_genfs, node, reclaim);
    292 
    293 	pu = puffs_init(pops, _PATH_PUFFS, "sysctlfs", NULL, pflags);
    294 	if (pu == NULL)
    295 		err(EXIT_FAILURE, "puffs_init");
    296 
    297 	puffs_set_pathbuild(pu, sysctlfs_pathbuild);
    298 	puffs_set_pathtransform(pu, sysctlfs_pathtransform);
    299 	puffs_set_pathcmp(pu, sysctlfs_pathcmp);
    300 	puffs_set_pathfree(pu, sysctlfs_pathfree);
    301 
    302 	puffs_setfhsize(pu, sizeof(struct sfsfid), PUFFS_FHFLAG_NFSV3);
    303 
    304 	if (sysctlfs_domount(pu) != 0)
    305 		errx(EXIT_FAILURE, "domount");
    306 
    307 	if (detach)
    308 		if (puffs_daemon(pu, 1, 1) == -1)
    309 			err(EXIT_FAILURE, "puffs_daemon");
    310 
    311 #ifdef RUMP_ACTION
    312 	{
    313 		extern int puffs_fakecc;
    314 		puffs_fakecc = 1;
    315 		rump_init();
    316 	}
    317 #endif
    318 
    319 	if (puffs_mount(pu, argv[1], mntflags, puffs_getroot(pu)) == -1)
    320 		err(EXIT_FAILURE, "puffs_mount");
    321 	if (puffs_mainloop(pu) == -1)
    322 		err(EXIT_FAILURE, "mainloop");
    323 
    324 	return 0;
    325 }
    326 
    327 static int
    328 sysctlfs_domount(struct puffs_usermount *pu)
    329 {
    330 	struct puffs_pathobj *po_root;
    331 	struct puffs_node *pn_root;
    332 	struct timeval tv_now;
    333 
    334 	rn.myid = 2;
    335 	rn.sysctl_flags = CTLTYPE_NODE;
    336 
    337 	gettimeofday(&tv_now, NULL);
    338 	TIMEVAL_TO_TIMESPEC(&tv_now, &fstime);
    339 
    340 	pn_root = puffs_pn_new(pu, &rn);
    341 	assert(pn_root != NULL);
    342 	puffs_setroot(pu, pn_root);
    343 
    344 	po_root = puffs_getrootpathobj(pu);
    345 	po_root->po_path = &sname_root;
    346 	po_root->po_len = 0;
    347 
    348 	fileuid = geteuid();
    349 	filegid = getegid();
    350 
    351 	if (fileuid == 0)
    352 		fileperms = 0755;
    353 	else
    354 		fileperms = 0555;
    355 
    356 	return 0;
    357 }
    358 
    359 int
    360 sysctlfs_fs_fhtonode(struct puffs_usermount *pu, void *fid, size_t fidsize,
    361 	struct puffs_newinfo *pni)
    362 {
    363 	struct puffs_pathobj po;
    364 	struct puffs_node *pn;
    365 	struct sfsnode *sfs;
    366 	struct sfsfid *sfid;
    367 
    368 	sfid = fid;
    369 
    370 	po.po_len = sfid->len;
    371 	po.po_path = &sfid->path;
    372 
    373 	pn = getnode(pu, &po, 0);
    374 	if (pn == NULL)
    375 		return EINVAL;
    376 	sfs = pn->pn_data;
    377 
    378 	puffs_newinfo_setcookie(pni, pn);
    379 	if (ISADIR(sfs))
    380 		puffs_newinfo_setvtype(pni, VDIR);
    381 	else
    382 		puffs_newinfo_setvtype(pni, VREG);
    383 
    384 	return 0;
    385 }
    386 
    387 int
    388 sysctlfs_fs_nodetofh(struct puffs_usermount *pu, void *cookie,
    389 	void *fid, size_t *fidsize)
    390 {
    391 	struct puffs_node *pn = cookie;
    392 	struct sfsfid *sfid;
    393 
    394 	sfid = fid;
    395 	sfid->len = PNPLEN(pn);
    396 	memcpy(&sfid->path, PNPATH(pn), sfid->len * sizeof(int));
    397 
    398 	return 0;
    399 }
    400 
    401 static void
    402 getnodedata(struct sfsnode *sfs, struct puffs_pathobj *po,
    403 	char *buf, size_t *bufsize)
    404 {
    405 	size_t sz;
    406 	int error = 0;
    407 
    408 	assert(!ISADIR(sfs));
    409 
    410 	memset(buf, 0, *bufsize);
    411 	switch (SYSCTL_TYPE(sfs->sysctl_flags)) {
    412 	case CTLTYPE_BOOL: {
    413 		bool b;
    414 		sz = sizeof(bool);
    415 		assert(sz <= *bufsize);
    416 		if (sysctl(po->po_path, po->po_len, &b, &sz, NULL, 0) == -1) {
    417 			error = errno;
    418 			break;
    419 		}
    420 		if (rflag)
    421 			memcpy(buf, &b, sz);
    422 		else
    423 			snprintf(buf, *bufsize, "%s", b ? "true" : "false");
    424 		break;
    425 	}
    426 	case CTLTYPE_INT: {
    427 		int i;
    428 		sz = sizeof(int);
    429 		assert(sz <= *bufsize);
    430 		if (sysctl(po->po_path, po->po_len, &i, &sz, NULL, 0) == -1) {
    431 			error = errno;
    432 			break;
    433 		}
    434 		if (rflag)
    435 			memcpy(buf, &i, sz);
    436 		else
    437 			snprintf(buf, *bufsize, "%d", i);
    438 		break;
    439 	}
    440 	case CTLTYPE_QUAD: {
    441 		quad_t q;
    442 		sz = sizeof(q);
    443 		assert(sz <= *bufsize);
    444 		if (sysctl(po->po_path, po->po_len, &q, &sz, NULL, 0) == -1) {
    445 			error = errno;
    446 			break;
    447 		}
    448 		if (rflag)
    449 			memcpy(buf, &q, sz);
    450 		else
    451 			snprintf(buf, *bufsize, "%" PRId64, q);
    452 		break;
    453 	}
    454 	case CTLTYPE_STRUCT: {
    455 		uint8_t snode[SFS_MAXFILE/2-1];
    456 		unsigned i;
    457 
    458 		sz = sizeof(snode);
    459 		assert(sz <= *bufsize);
    460 		if (sysctl(po->po_path, po->po_len, snode, &sz, NULL, 0) == -1){
    461 			error = errno;
    462 			break;
    463 		}
    464 		if (rflag) {
    465 			memcpy(buf, &snode, sz);
    466 		} else {
    467 			for (i = 0; i < sz && 2*i < *bufsize; i++) {
    468 				sprintf(&buf[2*i], "%02x", snode[i]);
    469 			}
    470 			buf[2*i] = '\0';
    471 		}
    472 		break;
    473 	}
    474 	case CTLTYPE_STRING: {
    475 		sz = *bufsize;
    476 		assert(sz <= *bufsize);
    477 		if (sysctl(po->po_path, po->po_len, buf, &sz, NULL, 0) == -1) {
    478 			error = errno;
    479 			break;
    480 		}
    481 		break;
    482 	}
    483 	default:
    484 		snprintf(buf, *bufsize, "invalid sysctl CTLTYPE %d",
    485 		    SYSCTL_TYPE(sfs->sysctl_flags));
    486 		break;
    487 	}
    488 
    489 	if (error) {
    490 		*bufsize = 0;
    491 		return;
    492 	}
    493 
    494 	if (rflag)
    495 		*bufsize = sz;
    496 	else
    497 		*bufsize = strlen(buf);
    498 }
    499 
    500 static int
    501 getlinks(struct sfsnode *sfs, struct puffs_pathobj *po)
    502 {
    503 	struct sysctlnode sn[SFS_NODEPERDIR];
    504 	struct sysctlnode qnode;
    505 	SfsName *sname;
    506 	size_t sl;
    507 
    508 	if (!ISADIR(sfs))
    509 		return 1;
    510 
    511 	memset(&qnode, 0, sizeof(qnode));
    512 	sl = sizeof(sn);
    513 	qnode.sysctl_flags = SYSCTL_VERSION;
    514 	sname = po->po_path;
    515 	(*sname)[po->po_len] = CTL_QUERY;
    516 
    517 	if (sysctl(*sname, po->po_len + 1, sn, &sl,
    518 	    &qnode, sizeof(qnode)) == -1)
    519 		return 0;
    520 
    521 	return (sl / sizeof(sn[0])) + 2;
    522 }
    523 
    524 static int
    525 getsize(struct sfsnode *sfs, struct puffs_pathobj *po)
    526 {
    527 	char buf[SFS_MAXFILE];
    528 	size_t sz = sizeof(buf);
    529 
    530 	if (ISADIR(sfs))
    531 		return getlinks(sfs, po) * 16; /* totally arbitrary */
    532 
    533 	getnodedata(sfs, po, buf, &sz);
    534 	if (rflag)
    535 		return sz;
    536 	else
    537 		return sz + 1; /* for \n, not \0 */
    538 }
    539 
    540 int
    541 sysctlfs_node_lookup(struct puffs_usermount *pu, void *opc,
    542 	struct puffs_newinfo *pni, const struct puffs_cn *pcn)
    543 {
    544 	struct puffs_cn *p2cn = __UNCONST(pcn); /* XXX: fix the interface */
    545 	struct sysctlnode sn[SFS_NODEPERDIR];
    546 	struct sysctlnode qnode;
    547 	struct puffs_node *pn_dir = opc;
    548 	struct puffs_node *pn_new;
    549 	struct sfsnode *sfs_dir = pn_dir->pn_data, *sfs_new;
    550 	SfsName *sname = PCNPATH(pcn);
    551 	size_t sl, i;
    552 	int nodetype;
    553 
    554 	assert(ISADIR(sfs_dir));
    555 
    556 	/*
    557 	 * If we're looking for dotdot, we already have the entire pathname
    558 	 * in sname, courtesy of pathbuild, so we can skip this step.
    559 	 */
    560 	if (!PCNISDOTDOT(pcn)) {
    561 		memset(&qnode, 0, sizeof(qnode));
    562 		sl = SFS_NODEPERDIR * sizeof(struct sysctlnode);
    563 		qnode.sysctl_flags = SYSCTL_VERSION;
    564 		(*sname)[PCNPLEN(pcn)] = CTL_QUERY;
    565 
    566 		if (sysctl(*sname, PCNPLEN(pcn) + 1, sn, &sl,
    567 		    &qnode, sizeof(qnode)) == -1)
    568 			return ENOENT;
    569 
    570 		for (i = 0; i < sl / sizeof(struct sysctlnode); i++)
    571 			if (strcmp(sn[i].sysctl_name, pcn->pcn_name) == 0)
    572 				break;
    573 		if (i == sl / sizeof(struct sysctlnode))
    574 			return ENOENT;
    575 
    576 		(*sname)[PCNPLEN(pcn)] = sn[i].sysctl_num;
    577 		p2cn->pcn_po_full.po_len++;
    578 		nodetype = sn[i].sysctl_flags;
    579 	} else
    580 		nodetype = CTLTYPE_NODE;
    581 
    582 	pn_new = getnode(pu, &p2cn->pcn_po_full, nodetype);
    583 	sfs_new = pn_new->pn_data;
    584 
    585 	puffs_newinfo_setcookie(pni, pn_new);
    586 	if (ISADIR(sfs_new))
    587 		puffs_newinfo_setvtype(pni, VDIR);
    588 	else
    589 		puffs_newinfo_setvtype(pni, VREG);
    590 
    591 	return 0;
    592 }
    593 
    594 int
    595 sysctlfs_node_getattr(struct puffs_usermount *pu, void *opc, struct vattr *va,
    596 	const struct puffs_cred *pcr)
    597 {
    598 	struct puffs_node *pn = opc;
    599 	struct sfsnode *sfs = pn->pn_data;
    600 
    601 	memset(va, 0, sizeof(struct vattr));
    602 
    603 	if (ISADIR(sfs)) {
    604 		va->va_type = VDIR;
    605 		va->va_mode = 0555;
    606 	} else {
    607 		va->va_type = VREG;
    608 		va->va_mode = fileperms;
    609 	}
    610 	va->va_uid = fileuid;
    611 	va->va_gid = filegid;
    612 	va->va_nlink = getlinks(sfs, &pn->pn_po);
    613 	va->va_fileid = sfs->myid;
    614 	va->va_size = getsize(sfs, &pn->pn_po);
    615 	va->va_gen = 1;
    616 	va->va_rdev = PUFFS_VNOVAL;
    617 	va->va_blocksize = 512;
    618 	va->va_filerev = 1;
    619 
    620 	va->va_atime = va->va_mtime = va->va_ctime = va->va_birthtime = fstime;
    621 
    622 	return 0;
    623 }
    624 
    625 int
    626 sysctlfs_node_setattr(struct puffs_usermount *pu, void *opc,
    627 	const struct vattr *va, const struct puffs_cred *pcr)
    628 {
    629 
    630 	/* dummy, but required for write */
    631 	/* XXX: we could return EOPNOTSUPP or something */
    632 	return 0;
    633 }
    634 
    635 int
    636 sysctlfs_node_readdir(struct puffs_usermount *pu, void *opc,
    637 	struct dirent *dent, off_t *readoff, size_t *reslen,
    638 	const struct puffs_cred *pcr, int *eofflag,
    639 	off_t *cookies, size_t *ncookies)
    640 {
    641 	struct sysctlnode sn[SFS_NODEPERDIR];
    642 	struct sysctlnode qnode;
    643 	struct puffs_node *pn_dir = opc;
    644 	struct puffs_node *pn_res;
    645 	struct puffs_pathobj po;
    646 	struct sfsnode *sfs_dir = pn_dir->pn_data, *sfs_ent;
    647 	SfsName *sname;
    648 	size_t sl, i;
    649 	enum vtype vt;
    650 	ino_t id;
    651 
    652 	*ncookies = 0;
    653 
    654  again:
    655 	if (*readoff == DENT_DOT || *readoff == DENT_DOTDOT) {
    656 		puffs_gendotdent(&dent, sfs_dir->myid, *readoff, reslen);
    657 		(*readoff)++;
    658 		PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff);
    659 		goto again;
    660 	}
    661 
    662 	memset(&qnode, 0, sizeof(qnode));
    663 	sl = SFS_NODEPERDIR * sizeof(struct sysctlnode);
    664 	qnode.sysctl_flags = SYSCTL_VERSION;
    665 	sname = PNPATH(pn_dir);
    666 	(*sname)[PNPLEN(pn_dir)] = CTL_QUERY;
    667 
    668 	if (sysctl(*sname, PNPLEN(pn_dir) + 1, sn, &sl,
    669 	    &qnode, sizeof(qnode)) == -1)
    670 		return ENOENT;
    671 
    672 	po.po_path = sname;
    673 	po.po_len = PNPLEN(pn_dir)+1;
    674 
    675 	for (i = DENT_ADJ(*readoff); i < sl / sizeof(struct sysctlnode); i++) {
    676 		if (SYSCTL_TYPE(sn[i].sysctl_flags) == CTLTYPE_NODE)
    677 			vt = VDIR;
    678 		else
    679 			vt = VREG;
    680 
    681 		/*
    682 		 * check if the node exists.  if so, give it the real
    683 		 * inode number.  otherwise just fake it.
    684 		 */
    685 		(*sname)[PNPLEN(pn_dir)] = sn[i].sysctl_num;
    686 		pn_res = puffs_pn_nodewalk(pu, puffs_path_walkcmp, &po);
    687 		if (pn_res) {
    688 			sfs_ent = pn_res->pn_data;
    689 			id = sfs_ent->myid;
    690 		} else {
    691 			id = nextid++;
    692 		}
    693 
    694 		if (!puffs_nextdent(&dent, sn[i].sysctl_name, id,
    695 		    puffs_vtype2dt(vt), reslen))
    696 			return 0;
    697 
    698 		(*readoff)++;
    699 		PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff);
    700 	}
    701 
    702 	*eofflag = 1;
    703 	return 0;
    704 }
    705 
    706 int
    707 sysctlfs_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf,
    708 	off_t offset, size_t *resid, const struct puffs_cred *pcr,
    709 	int ioflag)
    710 {
    711 	char localbuf[SFS_MAXFILE];
    712 	struct puffs_node *pn = opc;
    713 	struct sfsnode *sfs = pn->pn_data;
    714 	size_t sz = sizeof(localbuf);
    715 	int xfer;
    716 
    717 	if (ISADIR(sfs))
    718 		return EISDIR;
    719 
    720 	getnodedata(sfs, &pn->pn_po, localbuf, &sz);
    721 	if ((ssize_t)sz < offset)
    722 		xfer = 0;
    723 	else
    724 		xfer = MIN(*resid, sz - offset);
    725 
    726 	if (xfer <= 0)
    727 		return 0;
    728 
    729 	memcpy(buf, localbuf + offset, xfer);
    730 	*resid -= xfer;
    731 
    732 	if (*resid && !rflag) {
    733 		buf[xfer] = '\n';
    734 		(*resid)--;
    735 	}
    736 
    737 	return 0;
    738 }
    739 
    740 int
    741 sysctlfs_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf,
    742 	off_t offset, size_t *resid, const struct puffs_cred *cred,
    743 	int ioflag)
    744 {
    745 	struct puffs_node *pn = opc;
    746 	struct sfsnode *sfs = pn->pn_data;
    747 	long long ll;
    748 	int i, rv;
    749 	bool b;
    750 
    751 	/*
    752 	 * I picked the wrong day to ... um, the wrong place to return errors
    753 	 */
    754 
    755 	/* easy to support, but just unavailable now */
    756 	if (rflag)
    757 		return EOPNOTSUPP;
    758 
    759 	if (puffs_cred_isjuggernaut(cred) == 0)
    760 		return EACCES;
    761 
    762 	if (ISADIR(sfs))
    763 		return EISDIR;
    764 
    765 	if (offset != 0)
    766 		return EINVAL;
    767 
    768 	if (ioflag & PUFFS_IO_APPEND)
    769 		return EINVAL;
    770 
    771 	switch (SYSCTL_TYPE(sfs->sysctl_flags)) {
    772 	case CTLTYPE_BOOL:
    773 		if (strcasestr((const char *)buf, "true"))
    774 			b = true;
    775 		else if (strcasestr((const char *)buf, "false"))
    776 			b = false;
    777 		else
    778 			return EINVAL;
    779 		rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL,
    780 		    &b, sizeof(b));
    781 		break;
    782 	case CTLTYPE_INT:
    783 		if (sscanf((const char *)buf, "%d", &i) != 1)
    784 			return EINVAL;
    785 		rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL,
    786 		    &i, sizeof(int));
    787 		break;
    788 	case CTLTYPE_QUAD:
    789 		if (sscanf((const char *)buf, "%lld", &ll) != 1)
    790 			return EINVAL;
    791 		rv =  sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL,
    792 		    &ll, sizeof(long long));
    793 		break;
    794 	case CTLTYPE_STRING:
    795 		rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL, buf, *resid);
    796 		break;
    797 	default:
    798 		rv = EINVAL;
    799 		break;
    800 	}
    801 
    802 	if (rv)
    803 		return rv;
    804 
    805 	*resid = 0;
    806 	return 0;
    807 }
    808