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