Home | History | Annotate | Line # | Download | only in mount_sysctlfs
sysctlfs.c revision 1.17
      1 /*	$NetBSD: sysctlfs.c,v 1.17 2012/11/04 22:47:21 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.17 2012/11/04 22:47:21 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 		/*
    185 		 * don't know nodetype?  query...
    186 		 *
    187 		 * XXX1: nothing really guarantees 0 is an invalid nodetype
    188 		 * XXX2: is there really no easier way of doing this?  we
    189 		 *       know the whole mib path
    190 		 */
    191 		if (!nodetype) {
    192 			sname = po->po_path;
    193 			memcpy(myname, po->po_path, po->po_len * sizeof(int));
    194 
    195 			memset(&qnode, 0, sizeof(qnode));
    196 			qnode.sysctl_flags = SYSCTL_VERSION;
    197 			myname[po->po_len-1] = CTL_QUERY;
    198 
    199 			sl = sizeof(sn);
    200 			if (sysctl(myname, po->po_len, sn, &sl,
    201 			    &qnode, sizeof(qnode)) == -1)
    202 				abort();
    203 
    204 			for (i = 0; i < sl / sizeof(struct sysctlnode); i++) {
    205 				 if (sn[i].sysctl_num==(*sname)[po->po_len-1]) {
    206 					nodetype = sn[i].sysctl_flags;
    207 					break;
    208 				}
    209 			}
    210 			if (!nodetype)
    211 				return NULL;
    212 		}
    213 
    214 		sfs = emalloc(sizeof(struct sfsnode));
    215 		sfs->sysctl_flags = nodetype;
    216 		sfs->myid = nextid++;
    217 
    218 		pn = puffs_pn_new(pu, sfs);
    219 		assert(pn);
    220 	}
    221 
    222 	return pn;
    223 }
    224 
    225 int
    226 main(int argc, char *argv[])
    227 {
    228 	struct puffs_usermount *pu;
    229 	struct puffs_ops *pops;
    230 	mntoptparse_t mp;
    231 	int mntflags, pflags;
    232 	int detach;
    233 	int ch;
    234 
    235 	setprogname(argv[0]);
    236 
    237 	if (argc < 2)
    238 		errx(1, "usage: %s sysctlfs [-o mntopts] mountpath",
    239 		    getprogname());
    240 
    241 	mntflags = pflags = 0;
    242 	detach = 1;
    243 	while ((ch = getopt(argc, argv, "o:rs")) != -1) {
    244 		switch (ch) {
    245 		case 'o':
    246 			mp = getmntopts(optarg, puffsmopts, &mntflags, &pflags);
    247 			if (mp == NULL)
    248 				err(1, "getmntopts");
    249 			freemntopts(mp);
    250 			break;
    251 		case 'r':
    252 			rflag = 1;
    253 			break;
    254 		case 's':
    255 			detach = 0;
    256 			break;
    257 		}
    258 	}
    259 	argv += optind;
    260 	argc -= optind;
    261 	pflags |= PUFFS_FLAG_BUILDPATH | PUFFS_KFLAG_NOCACHE;
    262 
    263 	if (pflags & PUFFS_FLAG_OPDUMP)
    264 		detach = 0;
    265 
    266 	if (argc != 2)
    267 		errx(1, "usage: %s [-o mntopts] mountpath", getprogname());
    268 
    269 	PUFFSOP_INIT(pops);
    270 
    271 	PUFFSOP_SETFSNOP(pops, unmount);
    272 	PUFFSOP_SETFSNOP(pops, sync);
    273 	PUFFSOP_SETFSNOP(pops, statvfs);
    274 	PUFFSOP_SET(pops, sysctlfs, fs, nodetofh);
    275 	PUFFSOP_SET(pops, sysctlfs, fs, fhtonode);
    276 
    277 	PUFFSOP_SET(pops, sysctlfs, node, lookup);
    278 	PUFFSOP_SET(pops, sysctlfs, node, getattr);
    279 	PUFFSOP_SET(pops, sysctlfs, node, setattr);
    280 	PUFFSOP_SET(pops, sysctlfs, node, readdir);
    281 	PUFFSOP_SET(pops, sysctlfs, node, read);
    282 	PUFFSOP_SET(pops, sysctlfs, node, write);
    283 	PUFFSOP_SET(pops, puffs_genfs, node, reclaim);
    284 
    285 	pu = puffs_init(pops, _PATH_PUFFS, "sysctlfs", NULL, pflags);
    286 	if (pu == NULL)
    287 		err(1, "puffs_init");
    288 
    289 	puffs_set_pathbuild(pu, sysctlfs_pathbuild);
    290 	puffs_set_pathtransform(pu, sysctlfs_pathtransform);
    291 	puffs_set_pathcmp(pu, sysctlfs_pathcmp);
    292 	puffs_set_pathfree(pu, sysctlfs_pathfree);
    293 
    294 	puffs_setfhsize(pu, sizeof(struct sfsfid), PUFFS_FHFLAG_NFSV3);
    295 
    296 	if (sysctlfs_domount(pu) != 0)
    297 		errx(1, "domount");
    298 
    299 	if (detach)
    300 		if (puffs_daemon(pu, 1, 1) == -1)
    301 			err(1, "puffs_daemon");
    302 
    303 #ifdef RUMP_ACTION
    304 	{
    305 	extern int puffs_fakecc;
    306 	puffs_fakecc = 1;
    307 	rump_init();
    308 	}
    309 #endif
    310 
    311 	if (puffs_mount(pu, argv[1], mntflags, puffs_getroot(pu)) == -1)
    312 		err(1, "puffs_mount");
    313 	if (puffs_mainloop(pu) == -1)
    314 		err(1, "mainloop");
    315 
    316 	return 0;
    317 }
    318 
    319 static int
    320 sysctlfs_domount(struct puffs_usermount *pu)
    321 {
    322 	struct puffs_pathobj *po_root;
    323 	struct puffs_node *pn_root;
    324 	struct timeval tv_now;
    325 
    326 	rn.myid = 2;
    327 	rn.sysctl_flags = CTLTYPE_NODE;
    328 
    329 	gettimeofday(&tv_now, NULL);
    330 	TIMEVAL_TO_TIMESPEC(&tv_now, &fstime);
    331 
    332 	pn_root = puffs_pn_new(pu, &rn);
    333 	assert(pn_root != NULL);
    334 	puffs_setroot(pu, pn_root);
    335 
    336 	po_root = puffs_getrootpathobj(pu);
    337 	po_root->po_path = &sname_root;
    338 	po_root->po_len = 0;
    339 
    340 	fileuid = geteuid();
    341 	filegid = getegid();
    342 
    343 	if (fileuid == 0)
    344 		fileperms = 0755;
    345 	else
    346 		fileperms = 0555;
    347 
    348 	return 0;
    349 }
    350 
    351 int
    352 sysctlfs_fs_fhtonode(struct puffs_usermount *pu, void *fid, size_t fidsize,
    353 	struct puffs_newinfo *pni)
    354 {
    355 	struct puffs_pathobj po;
    356 	struct puffs_node *pn;
    357 	struct sfsnode *sfs;
    358 	struct sfsfid *sfid;
    359 
    360 	sfid = fid;
    361 
    362 	po.po_len = sfid->len;
    363 	po.po_path = &sfid->path;
    364 
    365 	pn = getnode(pu, &po, 0);
    366 	if (pn == NULL)
    367 		return EINVAL;
    368 	sfs = pn->pn_data;
    369 
    370 	puffs_newinfo_setcookie(pni, pn);
    371 	if (ISADIR(sfs))
    372 		puffs_newinfo_setvtype(pni, VDIR);
    373 	else
    374 		puffs_newinfo_setvtype(pni, VREG);
    375 
    376 	return 0;
    377 }
    378 
    379 int
    380 sysctlfs_fs_nodetofh(struct puffs_usermount *pu, void *cookie,
    381 	void *fid, size_t *fidsize)
    382 {
    383 	struct puffs_node *pn = cookie;
    384 	struct sfsfid *sfid;
    385 
    386 	sfid = fid;
    387 	sfid->len = PNPLEN(pn);
    388 	memcpy(&sfid->path, PNPATH(pn), sfid->len * sizeof(int));
    389 
    390 	return 0;
    391 }
    392 
    393 static void
    394 getnodedata(struct sfsnode *sfs, struct puffs_pathobj *po,
    395 	char *buf, size_t *bufsize)
    396 {
    397 	size_t sz;
    398 	int error = 0;
    399 
    400 	assert(!ISADIR(sfs));
    401 
    402 	memset(buf, 0, *bufsize);
    403 	switch (SYSCTL_TYPE(sfs->sysctl_flags)) {
    404 	case CTLTYPE_BOOL: {
    405 		bool b;
    406 		sz = sizeof(bool);
    407 		assert(sz <= *bufsize);
    408 		if (sysctl(po->po_path, po->po_len, &b, &sz, NULL, 0) == -1) {
    409 			error = errno;
    410 			break;
    411 		}
    412 		if (rflag)
    413 			memcpy(buf, &b, sz);
    414 		else
    415 			snprintf(buf, *bufsize, "%s", b ? "true" : "false");
    416 		break;
    417 	}
    418 	case CTLTYPE_INT: {
    419 		int i;
    420 		sz = sizeof(int);
    421 		assert(sz <= *bufsize);
    422 		if (sysctl(po->po_path, po->po_len, &i, &sz, NULL, 0) == -1) {
    423 			error = errno;
    424 			break;
    425 		}
    426 		if (rflag)
    427 			memcpy(buf, &i, sz);
    428 		else
    429 			snprintf(buf, *bufsize, "%d", i);
    430 		break;
    431 	}
    432 	case CTLTYPE_QUAD: {
    433 		quad_t q;
    434 		sz = sizeof(q);
    435 		assert(sz <= *bufsize);
    436 		if (sysctl(po->po_path, po->po_len, &q, &sz, NULL, 0) == -1) {
    437 			error = errno;
    438 			break;
    439 		}
    440 		if (rflag)
    441 			memcpy(buf, &q, sz);
    442 		else
    443 			snprintf(buf, *bufsize, "%" PRId64, q);
    444 		break;
    445 	}
    446 	case CTLTYPE_STRUCT: {
    447 		uint8_t snode[SFS_MAXFILE/2-1];
    448 		unsigned i;
    449 
    450 		sz = sizeof(snode);
    451 		assert(sz <= *bufsize);
    452 		if (sysctl(po->po_path, po->po_len, snode, &sz, NULL, 0) == -1){
    453 			error = errno;
    454 			break;
    455 		}
    456 		if (rflag) {
    457 			memcpy(buf, &snode, sz);
    458 		} else {
    459 			for (i = 0; i < sz && 2*i < *bufsize; i++) {
    460 				sprintf(&buf[2*i], "%02x", snode[i]);
    461 			}
    462 			buf[2*i] = '\0';
    463 		}
    464 		break;
    465 	}
    466 	case CTLTYPE_STRING: {
    467 		sz = *bufsize;
    468 		assert(sz <= *bufsize);
    469 		if (sysctl(po->po_path, po->po_len, buf, &sz, NULL, 0) == -1) {
    470 			error = errno;
    471 			break;
    472 		}
    473 		break;
    474 	}
    475 	default:
    476 		snprintf(buf, *bufsize, "invalid sysctl CTLTYPE %d",
    477 		    SYSCTL_TYPE(sfs->sysctl_flags));
    478 		break;
    479 	}
    480 
    481 	if (error) {
    482 		*bufsize = 0;
    483 		return;
    484 	}
    485 
    486 	if (rflag)
    487 		*bufsize = sz;
    488 	else
    489 		*bufsize = strlen(buf);
    490 }
    491 
    492 static int
    493 getlinks(struct sfsnode *sfs, struct puffs_pathobj *po)
    494 {
    495 	struct sysctlnode sn[SFS_NODEPERDIR];
    496 	struct sysctlnode qnode;
    497 	SfsName *sname;
    498 	size_t sl;
    499 
    500 	if (!ISADIR(sfs))
    501 		return 1;
    502 
    503 	memset(&qnode, 0, sizeof(qnode));
    504 	sl = sizeof(sn);
    505 	qnode.sysctl_flags = SYSCTL_VERSION;
    506 	sname = po->po_path;
    507 	(*sname)[po->po_len] = CTL_QUERY;
    508 
    509 	if (sysctl(*sname, po->po_len + 1, sn, &sl,
    510 	    &qnode, sizeof(qnode)) == -1)
    511 		return 0;
    512 
    513 	return (sl / sizeof(sn[0])) + 2;
    514 }
    515 
    516 static int
    517 getsize(struct sfsnode *sfs, struct puffs_pathobj *po)
    518 {
    519 	char buf[SFS_MAXFILE];
    520 	size_t sz = sizeof(buf);
    521 
    522 	if (ISADIR(sfs))
    523 		return getlinks(sfs, po) * 16; /* totally arbitrary */
    524 
    525 	getnodedata(sfs, po, buf, &sz);
    526 	if (rflag)
    527 		return sz;
    528 	else
    529 		return sz + 1; /* for \n, not \0 */
    530 }
    531 
    532 int
    533 sysctlfs_node_lookup(struct puffs_usermount *pu, void *opc,
    534 	struct puffs_newinfo *pni, const struct puffs_cn *pcn)
    535 {
    536 	struct puffs_cn *p2cn = __UNCONST(pcn); /* XXX: fix the interface */
    537 	struct sysctlnode sn[SFS_NODEPERDIR];
    538 	struct sysctlnode qnode;
    539 	struct puffs_node *pn_dir = opc;
    540 	struct puffs_node *pn_new;
    541 	struct sfsnode *sfs_dir = pn_dir->pn_data, *sfs_new;
    542 	SfsName *sname = PCNPATH(pcn);
    543 	size_t sl, i;
    544 	int nodetype;
    545 
    546 	assert(ISADIR(sfs_dir));
    547 
    548 	/*
    549 	 * If we're looking for dotdot, we already have the entire pathname
    550 	 * in sname, courtesy of pathbuild, so we can skip this step.
    551 	 */
    552 	if (!PCNISDOTDOT(pcn)) {
    553 		memset(&qnode, 0, sizeof(qnode));
    554 		sl = SFS_NODEPERDIR * sizeof(struct sysctlnode);
    555 		qnode.sysctl_flags = SYSCTL_VERSION;
    556 		(*sname)[PCNPLEN(pcn)] = CTL_QUERY;
    557 
    558 		if (sysctl(*sname, PCNPLEN(pcn) + 1, sn, &sl,
    559 		    &qnode, sizeof(qnode)) == -1)
    560 			return ENOENT;
    561 
    562 		for (i = 0; i < sl / sizeof(struct sysctlnode); i++)
    563 			if (strcmp(sn[i].sysctl_name, pcn->pcn_name) == 0)
    564 				break;
    565 		if (i == sl / sizeof(struct sysctlnode))
    566 			return ENOENT;
    567 
    568 		(*sname)[PCNPLEN(pcn)] = sn[i].sysctl_num;
    569 		p2cn->pcn_po_full.po_len++;
    570 		nodetype = sn[i].sysctl_flags;
    571 	} else
    572 		nodetype = CTLTYPE_NODE;
    573 
    574 	pn_new = getnode(pu, &p2cn->pcn_po_full, nodetype);
    575 	sfs_new = pn_new->pn_data;
    576 
    577 	puffs_newinfo_setcookie(pni, pn_new);
    578 	if (ISADIR(sfs_new))
    579 		puffs_newinfo_setvtype(pni, VDIR);
    580 	else
    581 		puffs_newinfo_setvtype(pni, VREG);
    582 
    583 	return 0;
    584 }
    585 
    586 int
    587 sysctlfs_node_getattr(struct puffs_usermount *pu, void *opc, struct vattr *va,
    588 	const struct puffs_cred *pcr)
    589 {
    590 	struct puffs_node *pn = opc;
    591 	struct sfsnode *sfs = pn->pn_data;
    592 
    593 	memset(va, 0, sizeof(struct vattr));
    594 
    595 	if (ISADIR(sfs)) {
    596 		va->va_type = VDIR;
    597 		va->va_mode = 0555;
    598 	} else {
    599 		va->va_type = VREG;
    600 		va->va_mode = fileperms;
    601 	}
    602 	va->va_uid = fileuid;
    603 	va->va_gid = filegid;
    604 	va->va_nlink = getlinks(sfs, &pn->pn_po);
    605 	va->va_fileid = sfs->myid;
    606 	va->va_size = getsize(sfs, &pn->pn_po);
    607 	va->va_gen = 1;
    608 	va->va_rdev = PUFFS_VNOVAL;
    609 	va->va_blocksize = 512;
    610 	va->va_filerev = 1;
    611 
    612 	va->va_atime = va->va_mtime = va->va_ctime = va->va_birthtime = fstime;
    613 
    614 	return 0;
    615 }
    616 
    617 int
    618 sysctlfs_node_setattr(struct puffs_usermount *pu, void *opc,
    619 	const struct vattr *va, const struct puffs_cred *pcr)
    620 {
    621 
    622 	/* dummy, but required for write */
    623 	/* XXX: we could return EOPNOTSUPP or something */
    624 	return 0;
    625 }
    626 
    627 int
    628 sysctlfs_node_readdir(struct puffs_usermount *pu, void *opc,
    629 	struct dirent *dent, off_t *readoff, size_t *reslen,
    630 	const struct puffs_cred *pcr, int *eofflag,
    631 	off_t *cookies, size_t *ncookies)
    632 {
    633 	struct sysctlnode sn[SFS_NODEPERDIR];
    634 	struct sysctlnode qnode;
    635 	struct puffs_node *pn_dir = opc;
    636 	struct puffs_node *pn_res;
    637 	struct puffs_pathobj po;
    638 	struct sfsnode *sfs_dir = pn_dir->pn_data, *sfs_ent;
    639 	SfsName *sname;
    640 	size_t sl, i;
    641 	enum vtype vt;
    642 	ino_t id;
    643 
    644 	*ncookies = 0;
    645 
    646  again:
    647 	if (*readoff == DENT_DOT || *readoff == DENT_DOTDOT) {
    648 		puffs_gendotdent(&dent, sfs_dir->myid, *readoff, reslen);
    649 		(*readoff)++;
    650 		PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff);
    651 		goto again;
    652 	}
    653 
    654 	memset(&qnode, 0, sizeof(qnode));
    655 	sl = SFS_NODEPERDIR * sizeof(struct sysctlnode);
    656 	qnode.sysctl_flags = SYSCTL_VERSION;
    657 	sname = PNPATH(pn_dir);
    658 	(*sname)[PNPLEN(pn_dir)] = CTL_QUERY;
    659 
    660 	if (sysctl(*sname, PNPLEN(pn_dir) + 1, sn, &sl,
    661 	    &qnode, sizeof(qnode)) == -1)
    662 		return ENOENT;
    663 
    664 	po.po_path = sname;
    665 	po.po_len = PNPLEN(pn_dir)+1;
    666 
    667 	for (i = DENT_ADJ(*readoff); i < sl / sizeof(struct sysctlnode); i++) {
    668 		if (SYSCTL_TYPE(sn[i].sysctl_flags) == CTLTYPE_NODE)
    669 			vt = VDIR;
    670 		else
    671 			vt = VREG;
    672 
    673 		/*
    674 		 * check if the node exists.  if so, give it the real
    675 		 * inode number.  otherwise just fake it.
    676 		 */
    677 		(*sname)[PNPLEN(pn_dir)] = sn[i].sysctl_num;
    678 		pn_res = puffs_pn_nodewalk(pu, puffs_path_walkcmp, &po);
    679 		if (pn_res) {
    680 			sfs_ent = pn_res->pn_data;
    681 			id = sfs_ent->myid;
    682 		} else {
    683 			id = nextid++;
    684 		}
    685 
    686 		if (!puffs_nextdent(&dent, sn[i].sysctl_name, id,
    687 		    puffs_vtype2dt(vt), reslen))
    688 			return 0;
    689 
    690 		(*readoff)++;
    691 		PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff);
    692 	}
    693 
    694 	*eofflag = 1;
    695 	return 0;
    696 }
    697 
    698 int
    699 sysctlfs_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf,
    700 	off_t offset, size_t *resid, const struct puffs_cred *pcr,
    701 	int ioflag)
    702 {
    703 	char localbuf[SFS_MAXFILE];
    704 	struct puffs_node *pn = opc;
    705 	struct sfsnode *sfs = pn->pn_data;
    706 	size_t sz = sizeof(localbuf);
    707 	int xfer;
    708 
    709 	if (ISADIR(sfs))
    710 		return EISDIR;
    711 
    712 	getnodedata(sfs, &pn->pn_po, localbuf, &sz);
    713 	if ((ssize_t)sz < offset)
    714 		xfer = 0;
    715 	else
    716 		xfer = MIN(*resid, sz - offset);
    717 
    718 	if (xfer <= 0)
    719 		return 0;
    720 
    721 	memcpy(buf, localbuf + offset, xfer);
    722 	*resid -= xfer;
    723 
    724 	if (*resid && !rflag) {
    725 		buf[xfer] = '\n';
    726 		(*resid)--;
    727 	}
    728 
    729 	return 0;
    730 }
    731 
    732 int
    733 sysctlfs_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf,
    734 	off_t offset, size_t *resid, const struct puffs_cred *cred,
    735 	int ioflag)
    736 {
    737 	struct puffs_node *pn = opc;
    738 	struct sfsnode *sfs = pn->pn_data;
    739 	long long ll;
    740 	int i, rv;
    741 	bool b;
    742 
    743 	/*
    744 	 * I picked the wrong day to ... um, the wrong place to return errors
    745 	 */
    746 
    747 	/* easy to support, but just unavailable now */
    748 	if (rflag)
    749 		return EOPNOTSUPP;
    750 
    751 	if (puffs_cred_isjuggernaut(cred) == 0)
    752 		return EACCES;
    753 
    754 	if (ISADIR(sfs))
    755 		return EISDIR;
    756 
    757 	if (offset != 0)
    758 		return EINVAL;
    759 
    760 	if (ioflag & PUFFS_IO_APPEND)
    761 		return EINVAL;
    762 
    763 	switch (SYSCTL_TYPE(sfs->sysctl_flags)) {
    764 	case CTLTYPE_BOOL:
    765 		if (strcasestr((const char *)buf, "true"))
    766 			b = true;
    767 		else if (strcasestr((const char *)buf, "false"))
    768 			b = false;
    769 		else
    770 			return EINVAL;
    771 		rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL,
    772 		    &b, sizeof(b));
    773 		break;
    774 	case CTLTYPE_INT:
    775 		if (sscanf((const char *)buf, "%d", &i) != 1)
    776 			return EINVAL;
    777 		rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL,
    778 		    &i, sizeof(int));
    779 		break;
    780 	case CTLTYPE_QUAD:
    781 		if (sscanf((const char *)buf, "%lld", &ll) != 1)
    782 			return EINVAL;
    783 		rv =  sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL,
    784 		    &ll, sizeof(long long));
    785 		break;
    786 	case CTLTYPE_STRING:
    787 		rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL, buf, *resid);
    788 		break;
    789 	default:
    790 		rv = EINVAL;
    791 		break;
    792 	}
    793 
    794 	if (rv)
    795 		return rv;
    796 
    797 	*resid = 0;
    798 	return 0;
    799 }
    800