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