1/* $NetBSD: sysctlfs.c,v 1.21 2023/04/02 18:23:02 ryo 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.21 2023/04/02 18:23:02 ryo 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 61PUFFSOP_PROTOS(sysctlfs) 62 63struct 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 72typedef int SfsName[N_HIERARCHY]; 73 74struct sfsfid { 75 int len; 76 SfsName path; 77}; 78 79static struct sfsnode rn; 80static SfsName sname_root; 81static struct timespec fstime; 82 83static ino_t nextid = 3; 84static mode_t fileperms; 85static uid_t fileuid; 86static gid_t filegid; 87 88static 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 94static int sysctlfs_domount(struct puffs_usermount *); 95 96/* 97 * build paths. doesn't support rename (but neither does the fs) 98 */ 99static int 100sysctlfs_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 126static int 127sysctlfs_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 146static int 147sysctlfs_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 156static void 157sysctlfs_pathfree(struct puffs_usermount *pu, struct puffs_pathobj *po) 158{ 159 160 free(po->po_path); 161} 162 163static struct puffs_node * 164getnode(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 pn; 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 225static void __dead 226usage(void) 227{ 228 229 fprintf(stderr, "Usage: %s [-o <mntopts>] sysctlfs mountpath\n", 230 getprogname()); 231 exit(1); 232} 233 234int 235main(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 327static int 328sysctlfs_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 = 0644; 353 else 354 fileperms = 0444; 355 356 return 0; 357} 358 359int 360sysctlfs_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 387int 388sysctlfs_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 401static void 402getnodedata(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 500static int 501getlinks(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 524static int 525getsize(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 540int 541sysctlfs_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 if (pn_new == NULL) 584 return ENOENT; 585 sfs_new = pn_new->pn_data; 586 587 puffs_newinfo_setcookie(pni, pn_new); 588 if (ISADIR(sfs_new)) 589 puffs_newinfo_setvtype(pni, VDIR); 590 else 591 puffs_newinfo_setvtype(pni, VREG); 592 593 return 0; 594} 595 596int 597sysctlfs_node_getattr(struct puffs_usermount *pu, void *opc, struct vattr *va, 598 const struct puffs_cred *pcr) 599{ 600 struct puffs_node *pn = opc; 601 struct sfsnode *sfs = pn->pn_data; 602 603 memset(va, 0, sizeof(struct vattr)); 604 605 if (ISADIR(sfs)) { 606 va->va_type = VDIR; 607 va->va_mode = 0555; 608 } else { 609 va->va_type = VREG; 610 va->va_mode = fileperms; 611 } 612 va->va_uid = fileuid; 613 va->va_gid = filegid; 614 va->va_nlink = getlinks(sfs, &pn->pn_po); 615 va->va_fileid = sfs->myid; 616 va->va_size = getsize(sfs, &pn->pn_po); 617 va->va_gen = 1; 618 va->va_rdev = PUFFS_VNOVAL; 619 va->va_blocksize = 512; 620 va->va_filerev = 1; 621 622 va->va_atime = va->va_mtime = va->va_ctime = va->va_birthtime = fstime; 623 624 return 0; 625} 626 627int 628sysctlfs_node_setattr(struct puffs_usermount *pu, void *opc, 629 const struct vattr *va, const struct puffs_cred *pcr) 630{ 631 632 /* dummy, but required for write */ 633 /* XXX: we could return EOPNOTSUPP or something */ 634 return 0; 635} 636 637int 638sysctlfs_node_readdir(struct puffs_usermount *pu, void *opc, 639 struct dirent *dent, off_t *readoff, size_t *reslen, 640 const struct puffs_cred *pcr, int *eofflag, 641 off_t *cookies, size_t *ncookies) 642{ 643 struct sysctlnode sn[SFS_NODEPERDIR]; 644 struct sysctlnode qnode; 645 struct puffs_node *pn_dir = opc; 646 struct puffs_node *pn_res; 647 struct puffs_pathobj po; 648 struct sfsnode *sfs_dir = pn_dir->pn_data, *sfs_ent; 649 SfsName *sname; 650 size_t sl, i; 651 enum vtype vt; 652 ino_t id; 653 654 *ncookies = 0; 655 656 again: 657 if (*readoff == DENT_DOT || *readoff == DENT_DOTDOT) { 658 puffs_gendotdent(&dent, sfs_dir->myid, *readoff, reslen); 659 (*readoff)++; 660 PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff); 661 goto again; 662 } 663 664 memset(&qnode, 0, sizeof(qnode)); 665 sl = SFS_NODEPERDIR * sizeof(struct sysctlnode); 666 qnode.sysctl_flags = SYSCTL_VERSION; 667 sname = PNPATH(pn_dir); 668 (*sname)[PNPLEN(pn_dir)] = CTL_QUERY; 669 670 if (sysctl(*sname, PNPLEN(pn_dir) + 1, sn, &sl, 671 &qnode, sizeof(qnode)) == -1) 672 return ENOENT; 673 674 po.po_path = sname; 675 po.po_len = PNPLEN(pn_dir)+1; 676 677 for (i = DENT_ADJ(*readoff); i < sl / sizeof(struct sysctlnode); i++) { 678 if (SYSCTL_TYPE(sn[i].sysctl_flags) == CTLTYPE_NODE) 679 vt = VDIR; 680 else 681 vt = VREG; 682 683 /* 684 * check if the node exists. if so, give it the real 685 * inode number. otherwise just fake it. 686 */ 687 (*sname)[PNPLEN(pn_dir)] = sn[i].sysctl_num; 688 pn_res = puffs_pn_nodewalk(pu, puffs_path_walkcmp, &po); 689 if (pn_res) { 690 sfs_ent = pn_res->pn_data; 691 id = sfs_ent->myid; 692 } else { 693 id = nextid++; 694 } 695 696 if (!puffs_nextdent(&dent, sn[i].sysctl_name, id, 697 puffs_vtype2dt(vt), reslen)) 698 return 0; 699 700 (*readoff)++; 701 PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff); 702 } 703 704 *eofflag = 1; 705 return 0; 706} 707 708int 709sysctlfs_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf, 710 off_t offset, size_t *resid, const struct puffs_cred *pcr, 711 int ioflag) 712{ 713 char localbuf[SFS_MAXFILE]; 714 struct puffs_node *pn = opc; 715 struct sfsnode *sfs = pn->pn_data; 716 size_t sz = sizeof(localbuf); 717 int xfer; 718 719 if (ISADIR(sfs)) 720 return EISDIR; 721 722 getnodedata(sfs, &pn->pn_po, localbuf, &sz); 723 if ((ssize_t)sz < offset) 724 xfer = 0; 725 else 726 xfer = MIN(*resid, sz - offset); 727 728 if (xfer <= 0) 729 return 0; 730 731 memcpy(buf, localbuf + offset, xfer); 732 *resid -= xfer; 733 734 if (*resid && !rflag) { 735 buf[xfer] = '\n'; 736 (*resid)--; 737 } 738 739 return 0; 740} 741 742int 743sysctlfs_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf, 744 off_t offset, size_t *resid, const struct puffs_cred *cred, 745 int ioflag) 746{ 747 struct puffs_node *pn = opc; 748 struct sfsnode *sfs = pn->pn_data; 749 long long ll; 750 int i, rv; 751 bool b; 752 753 /* 754 * I picked the wrong day to ... um, the wrong place to return errors 755 */ 756 757 /* easy to support, but just unavailable now */ 758 if (rflag) 759 return EOPNOTSUPP; 760 761 if (puffs_cred_isjuggernaut(cred) == 0) 762 return EACCES; 763 764 if (ISADIR(sfs)) 765 return EISDIR; 766 767 if (offset != 0) 768 return EINVAL; 769 770 if (ioflag & PUFFS_IO_APPEND) 771 return EINVAL; 772 773 switch (SYSCTL_TYPE(sfs->sysctl_flags)) { 774 case CTLTYPE_BOOL: 775 if (strcasestr((const char *)buf, "true")) 776 b = true; 777 else if (strcasestr((const char *)buf, "false")) 778 b = false; 779 else 780 return EINVAL; 781 rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL, 782 &b, sizeof(b)); 783 break; 784 case CTLTYPE_INT: 785 if (sscanf((const char *)buf, "%d", &i) != 1) 786 return EINVAL; 787 rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL, 788 &i, sizeof(int)); 789 break; 790 case CTLTYPE_QUAD: 791 if (sscanf((const char *)buf, "%lld", &ll) != 1) 792 return EINVAL; 793 rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL, 794 &ll, sizeof(long long)); 795 break; 796 case CTLTYPE_STRING: 797 rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL, buf, *resid); 798 break; 799 default: 800 rv = EINVAL; 801 break; 802 } 803 804 if (rv) 805 return rv; 806 807 *resid = 0; 808 return 0; 809} 810