1*95d71e0dSryo /* $NetBSD: sysctlfs.c,v 1.21 2023/04/02 18:23:02 ryo Exp $ */
2ba4e596bSpooka
304c15978Spooka /*-
4ba4e596bSpooka * Copyright (c) 2006, 2007 Antti Kantee. All Rights Reserved.
5ba4e596bSpooka *
6ba4e596bSpooka * Redistribution and use in source and binary forms, with or without
7ba4e596bSpooka * modification, are permitted provided that the following conditions
8ba4e596bSpooka * are met:
9ba4e596bSpooka * 1. Redistributions of source code must retain the above copyright
10ba4e596bSpooka * notice, this list of conditions and the following disclaimer.
11ba4e596bSpooka * 2. Redistributions in binary form must reproduce the above copyright
12ba4e596bSpooka * notice, this list of conditions and the following disclaimer in the
13ba4e596bSpooka * documentation and/or other materials provided with the distribution.
14ba4e596bSpooka *
15ba4e596bSpooka * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16ba4e596bSpooka * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17ba4e596bSpooka * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18ba4e596bSpooka * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19ba4e596bSpooka * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20ba4e596bSpooka * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21ba4e596bSpooka * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22ba4e596bSpooka * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23ba4e596bSpooka * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24ba4e596bSpooka * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25ba4e596bSpooka * SUCH DAMAGE.
26ba4e596bSpooka */
27ba4e596bSpooka
28ba4e596bSpooka /*
29ba4e596bSpooka * sysctlfs: mount sysctls as a file system tree. Supports query and
30ba4e596bSpooka * modify of nodes in the sysctl namespace in addition to namespace
31ba4e596bSpooka * traversal.
32ba4e596bSpooka */
33ba4e596bSpooka
34e0876492Spooka #include <sys/cdefs.h>
35e0876492Spooka #ifndef lint
36*95d71e0dSryo __RCSID("$NetBSD: sysctlfs.c,v 1.21 2023/04/02 18:23:02 ryo Exp $");
37e0876492Spooka #endif /* !lint */
38e0876492Spooka
39ba4e596bSpooka #include <sys/types.h>
40ba4e596bSpooka #include <sys/sysctl.h>
41ba4e596bSpooka
423b2f04fbSchristos #include <stdio.h>
43ba4e596bSpooka #include <assert.h>
44ba4e596bSpooka #include <err.h>
45ba4e596bSpooka #include <errno.h>
46ba4e596bSpooka #include <mntopts.h>
47ba4e596bSpooka #include <paths.h>
48ba4e596bSpooka #include <puffs.h>
49ba4e596bSpooka #include <stdlib.h>
50ba4e596bSpooka #include <string.h>
51ba4e596bSpooka #include <unistd.h>
52ba4e596bSpooka #include <util.h>
53ba4e596bSpooka
54c7528563Spooka #ifdef RUMP_ACTION
55c7528563Spooka #include <rump/rump.h>
56c7528563Spooka #include <rump/rump_syscalls.h>
57c7528563Spooka
58c7528563Spooka #define sysctl(a,b,c,d,e,f) rump_sys___sysctl(a,b,c,d,e,f)
59c7528563Spooka #endif
60c7528563Spooka
61ba4e596bSpooka PUFFSOP_PROTOS(sysctlfs)
62ba4e596bSpooka
63ba4e596bSpooka struct sfsnode {
64ba4e596bSpooka int sysctl_flags;
65ba4e596bSpooka ino_t myid;
66ba4e596bSpooka };
67ba4e596bSpooka
68ba4e596bSpooka #define SFSPATH_DOTDOT 0
69ba4e596bSpooka #define SFSPATH_NORMAL 1
70ba4e596bSpooka
71ba4e596bSpooka #define N_HIERARCHY 10
72ba4e596bSpooka typedef int SfsName[N_HIERARCHY];
73ba4e596bSpooka
74ba4e596bSpooka struct sfsfid {
75ba4e596bSpooka int len;
76ba4e596bSpooka SfsName path;
77ba4e596bSpooka };
78ba4e596bSpooka
7962234858Spooka static struct sfsnode rn;
8062234858Spooka static SfsName sname_root;
8162234858Spooka static struct timespec fstime;
82ba4e596bSpooka
8362234858Spooka static ino_t nextid = 3;
8462234858Spooka static mode_t fileperms;
8562234858Spooka static uid_t fileuid;
8662234858Spooka static gid_t filegid;
87ba4e596bSpooka
8804c15978Spooka static int rflag;
8904c15978Spooka
90ba4e596bSpooka #define ISADIR(a) ((SYSCTL_TYPE(a->sysctl_flags) == CTLTYPE_NODE))
91c8360f5cSpooka #define SFS_MAXFILE 32768
92ba4e596bSpooka #define SFS_NODEPERDIR 128
93ba4e596bSpooka
94ba4e596bSpooka static int sysctlfs_domount(struct puffs_usermount *);
95ba4e596bSpooka
96ba4e596bSpooka /*
97ba4e596bSpooka * build paths. doesn't support rename (but neither does the fs)
98ba4e596bSpooka */
99ba4e596bSpooka static int
sysctlfs_pathbuild(struct puffs_usermount * pu,const struct puffs_pathobj * parent,const struct puffs_pathobj * comp,size_t offset,struct puffs_pathobj * res)100ba4e596bSpooka sysctlfs_pathbuild(struct puffs_usermount *pu,
101ba4e596bSpooka const struct puffs_pathobj *parent, const struct puffs_pathobj *comp,
102ba4e596bSpooka size_t offset, struct puffs_pathobj *res)
103ba4e596bSpooka {
104ba4e596bSpooka SfsName *sname;
105ba4e596bSpooka size_t clen;
106ba4e596bSpooka
107ba4e596bSpooka assert(parent->po_len < N_HIERARCHY); /* code uses +1 */
108ba4e596bSpooka
109ba4e596bSpooka sname = malloc(sizeof(SfsName));
110ba4e596bSpooka assert(sname != NULL);
111ba4e596bSpooka
112ba4e596bSpooka clen = parent->po_len;
113ba4e596bSpooka if (comp->po_len == SFSPATH_DOTDOT) {
114ba4e596bSpooka assert(clen != 0);
115ba4e596bSpooka clen--;
116ba4e596bSpooka }
117ba4e596bSpooka
118ba4e596bSpooka memcpy(sname, parent->po_path, clen * sizeof(int));
119ba4e596bSpooka
120ba4e596bSpooka res->po_path = sname;
121ba4e596bSpooka res->po_len = clen;
122ba4e596bSpooka
123ba4e596bSpooka return 0;
124ba4e596bSpooka }
125ba4e596bSpooka
126ba4e596bSpooka static int
sysctlfs_pathtransform(struct puffs_usermount * pu,const struct puffs_pathobj * p,const struct puffs_cn * pcn,struct puffs_pathobj * res)127ba4e596bSpooka sysctlfs_pathtransform(struct puffs_usermount *pu,
12804c15978Spooka const struct puffs_pathobj *p, const struct puffs_cn *pcn,
129ba4e596bSpooka struct puffs_pathobj *res)
130ba4e596bSpooka {
131ba4e596bSpooka
132ba4e596bSpooka res->po_path = NULL;
133ba4e596bSpooka /*
134ba4e596bSpooka * XXX: overload. prevents us from doing rename, but the fs
135ba4e596bSpooka * (and sysctl(3)) doesn't support it, so no biggie
136ba4e596bSpooka */
137ba4e596bSpooka if (PCNISDOTDOT(pcn)) {
138ba4e596bSpooka res->po_len = SFSPATH_DOTDOT;
139ba4e596bSpooka }else {
140ba4e596bSpooka res->po_len = SFSPATH_NORMAL;
141ba4e596bSpooka }
142ba4e596bSpooka
143ba4e596bSpooka return 0;
144ba4e596bSpooka }
145ba4e596bSpooka
146ba4e596bSpooka static int
sysctlfs_pathcmp(struct puffs_usermount * pu,struct puffs_pathobj * po1,struct puffs_pathobj * po2,size_t clen,int checkprefix)147ba4e596bSpooka sysctlfs_pathcmp(struct puffs_usermount *pu, struct puffs_pathobj *po1,
148ba4e596bSpooka struct puffs_pathobj *po2, size_t clen, int checkprefix)
149ba4e596bSpooka {
150ba4e596bSpooka
151ba4e596bSpooka if (memcmp(po1->po_path, po2->po_path, clen * sizeof(int)) == 0)
152ba4e596bSpooka return 0;
153ba4e596bSpooka return 1;
154ba4e596bSpooka }
155ba4e596bSpooka
156ba4e596bSpooka static void
sysctlfs_pathfree(struct puffs_usermount * pu,struct puffs_pathobj * po)157ba4e596bSpooka sysctlfs_pathfree(struct puffs_usermount *pu, struct puffs_pathobj *po)
158ba4e596bSpooka {
159ba4e596bSpooka
160ba4e596bSpooka free(po->po_path);
161ba4e596bSpooka }
162ba4e596bSpooka
163ba4e596bSpooka static struct puffs_node *
getnode(struct puffs_usermount * pu,struct puffs_pathobj * po,int nodetype)164ba4e596bSpooka getnode(struct puffs_usermount *pu, struct puffs_pathobj *po, int nodetype)
165ba4e596bSpooka {
166ba4e596bSpooka struct sysctlnode sn[SFS_NODEPERDIR];
167ba4e596bSpooka struct sysctlnode qnode;
168ba4e596bSpooka struct puffs_node *pn;
169ba4e596bSpooka struct sfsnode *sfs;
170ba4e596bSpooka SfsName myname, *sname;
1713cafe960Slukem size_t sl, i;
172ba4e596bSpooka
173ba4e596bSpooka /*
174ba4e596bSpooka * Check if we need to create a new in-memory node or if we
175ba4e596bSpooka * already have one for this path. Shortcut for the rootnode.
176ba4e596bSpooka * Also, memcmp against zero-length would be quite true always.
177ba4e596bSpooka */
178ba4e596bSpooka if (po->po_len == 0)
179ba4e596bSpooka pn = puffs_getroot(pu);
180ba4e596bSpooka else
181ba4e596bSpooka pn = puffs_pn_nodewalk(pu, puffs_path_walkcmp, po);
182ba4e596bSpooka
18353500167Schristos if (pn != NULL)
18453500167Schristos return pn;
185ba4e596bSpooka /*
186ba4e596bSpooka * don't know nodetype? query...
187ba4e596bSpooka *
188ba4e596bSpooka * XXX1: nothing really guarantees 0 is an invalid nodetype
189ba4e596bSpooka * XXX2: is there really no easier way of doing this? we
190ba4e596bSpooka * know the whole mib path
191ba4e596bSpooka */
192ba4e596bSpooka if (!nodetype) {
193ba4e596bSpooka sname = po->po_path;
194411f3e27Schristos memcpy(myname, po->po_path, po->po_len * sizeof(myname[0]));
195ba4e596bSpooka
196ba4e596bSpooka memset(&qnode, 0, sizeof(qnode));
197ba4e596bSpooka qnode.sysctl_flags = SYSCTL_VERSION;
198ba4e596bSpooka myname[po->po_len-1] = CTL_QUERY;
199ba4e596bSpooka
200ba4e596bSpooka sl = sizeof(sn);
201ba4e596bSpooka if (sysctl(myname, po->po_len, sn, &sl,
202ba4e596bSpooka &qnode, sizeof(qnode)) == -1)
203ba4e596bSpooka abort();
204ba4e596bSpooka
205ba4e596bSpooka for (i = 0; i < sl / sizeof(struct sysctlnode); i++) {
206ba4e596bSpooka if (sn[i].sysctl_num == (*sname)[po->po_len-1]) {
207ba4e596bSpooka nodetype = sn[i].sysctl_flags;
208ba4e596bSpooka break;
209ba4e596bSpooka }
210ba4e596bSpooka }
211ba4e596bSpooka if (!nodetype)
212ba4e596bSpooka return NULL;
213ba4e596bSpooka }
214ba4e596bSpooka
215411f3e27Schristos sfs = emalloc(sizeof(*sfs));
216ba4e596bSpooka sfs->sysctl_flags = nodetype;
217ba4e596bSpooka sfs->myid = nextid++;
218ba4e596bSpooka
219ba4e596bSpooka pn = puffs_pn_new(pu, sfs);
220ba4e596bSpooka assert(pn);
221ba4e596bSpooka
222ba4e596bSpooka return pn;
223ba4e596bSpooka }
224ba4e596bSpooka
225411f3e27Schristos static void __dead
usage(void)226411f3e27Schristos usage(void)
227411f3e27Schristos {
228411f3e27Schristos
229*95d71e0dSryo fprintf(stderr, "Usage: %s [-o <mntopts>] sysctlfs mountpath\n",
230411f3e27Schristos getprogname());
231411f3e27Schristos exit(1);
232411f3e27Schristos }
233411f3e27Schristos
234ba4e596bSpooka int
main(int argc,char * argv[])235ba4e596bSpooka main(int argc, char *argv[])
236ba4e596bSpooka {
237ba4e596bSpooka struct puffs_usermount *pu;
238ba4e596bSpooka struct puffs_ops *pops;
239ba4e596bSpooka mntoptparse_t mp;
2404b0f2948Spooka int mntflags, pflags;
2414b0f2948Spooka int detach;
242ba4e596bSpooka int ch;
243ba4e596bSpooka
244ba4e596bSpooka setprogname(argv[0]);
245ba4e596bSpooka
246ba4e596bSpooka if (argc < 2)
247411f3e27Schristos usage();
248ba4e596bSpooka
2494b0f2948Spooka mntflags = pflags = 0;
2504b0f2948Spooka detach = 1;
25104c15978Spooka while ((ch = getopt(argc, argv, "o:rs")) != -1) {
252ba4e596bSpooka switch (ch) {
253ba4e596bSpooka case 'o':
254ba4e596bSpooka mp = getmntopts(optarg, puffsmopts, &mntflags, &pflags);
255ba4e596bSpooka if (mp == NULL)
256411f3e27Schristos err(EXIT_FAILURE, "getmntopts");
257ba4e596bSpooka freemntopts(mp);
258ba4e596bSpooka break;
25904c15978Spooka case 'r':
26004c15978Spooka rflag = 1;
26104c15978Spooka break;
262ba4e596bSpooka case 's':
2634b0f2948Spooka detach = 0;
264ba4e596bSpooka break;
265ba4e596bSpooka }
266ba4e596bSpooka }
267ba4e596bSpooka argv += optind;
268ba4e596bSpooka argc -= optind;
269ba4e596bSpooka pflags |= PUFFS_FLAG_BUILDPATH | PUFFS_KFLAG_NOCACHE;
270ba4e596bSpooka
271ba4e596bSpooka if (pflags & PUFFS_FLAG_OPDUMP)
2724b0f2948Spooka detach = 0;
273ba4e596bSpooka
274ba4e596bSpooka if (argc != 2)
275411f3e27Schristos usage();
276ba4e596bSpooka
277ba4e596bSpooka PUFFSOP_INIT(pops);
278ba4e596bSpooka
279ba4e596bSpooka PUFFSOP_SETFSNOP(pops, unmount);
280ba4e596bSpooka PUFFSOP_SETFSNOP(pops, sync);
281ba4e596bSpooka PUFFSOP_SETFSNOP(pops, statvfs);
282ba4e596bSpooka PUFFSOP_SET(pops, sysctlfs, fs, nodetofh);
283ba4e596bSpooka PUFFSOP_SET(pops, sysctlfs, fs, fhtonode);
284ba4e596bSpooka
285ba4e596bSpooka PUFFSOP_SET(pops, sysctlfs, node, lookup);
286ba4e596bSpooka PUFFSOP_SET(pops, sysctlfs, node, getattr);
287ba4e596bSpooka PUFFSOP_SET(pops, sysctlfs, node, setattr);
288ba4e596bSpooka PUFFSOP_SET(pops, sysctlfs, node, readdir);
289ba4e596bSpooka PUFFSOP_SET(pops, sysctlfs, node, read);
290ba4e596bSpooka PUFFSOP_SET(pops, sysctlfs, node, write);
291ba4e596bSpooka PUFFSOP_SET(pops, puffs_genfs, node, reclaim);
292ba4e596bSpooka
293ba4e596bSpooka pu = puffs_init(pops, _PATH_PUFFS, "sysctlfs", NULL, pflags);
294ba4e596bSpooka if (pu == NULL)
295411f3e27Schristos err(EXIT_FAILURE, "puffs_init");
296ba4e596bSpooka
297ba4e596bSpooka puffs_set_pathbuild(pu, sysctlfs_pathbuild);
298ba4e596bSpooka puffs_set_pathtransform(pu, sysctlfs_pathtransform);
299ba4e596bSpooka puffs_set_pathcmp(pu, sysctlfs_pathcmp);
300ba4e596bSpooka puffs_set_pathfree(pu, sysctlfs_pathfree);
301ba4e596bSpooka
302ba4e596bSpooka puffs_setfhsize(pu, sizeof(struct sfsfid), PUFFS_FHFLAG_NFSV3);
303ba4e596bSpooka
304ba4e596bSpooka if (sysctlfs_domount(pu) != 0)
305411f3e27Schristos errx(EXIT_FAILURE, "domount");
306ba4e596bSpooka
3074b0f2948Spooka if (detach)
3084462e945Spooka if (puffs_daemon(pu, 1, 1) == -1)
309411f3e27Schristos err(EXIT_FAILURE, "puffs_daemon");
3104b0f2948Spooka
311c7528563Spooka #ifdef RUMP_ACTION
312c7528563Spooka {
313c7528563Spooka extern int puffs_fakecc;
314c7528563Spooka puffs_fakecc = 1;
315c7528563Spooka rump_init();
316c7528563Spooka }
317c7528563Spooka #endif
318c7528563Spooka
319ec865a5bSpooka if (puffs_mount(pu, argv[1], mntflags, puffs_getroot(pu)) == -1)
320411f3e27Schristos err(EXIT_FAILURE, "puffs_mount");
3214b0f2948Spooka if (puffs_mainloop(pu) == -1)
322411f3e27Schristos err(EXIT_FAILURE, "mainloop");
323ba4e596bSpooka
324ba4e596bSpooka return 0;
325ba4e596bSpooka }
326ba4e596bSpooka
327ba4e596bSpooka static int
sysctlfs_domount(struct puffs_usermount * pu)328ba4e596bSpooka sysctlfs_domount(struct puffs_usermount *pu)
329ba4e596bSpooka {
330ba4e596bSpooka struct puffs_pathobj *po_root;
331ba4e596bSpooka struct puffs_node *pn_root;
332ba4e596bSpooka struct timeval tv_now;
333ba4e596bSpooka
334ba4e596bSpooka rn.myid = 2;
335ba4e596bSpooka rn.sysctl_flags = CTLTYPE_NODE;
336ba4e596bSpooka
337ba4e596bSpooka gettimeofday(&tv_now, NULL);
338ba4e596bSpooka TIMEVAL_TO_TIMESPEC(&tv_now, &fstime);
339ba4e596bSpooka
340ba4e596bSpooka pn_root = puffs_pn_new(pu, &rn);
341ba4e596bSpooka assert(pn_root != NULL);
342ba4e596bSpooka puffs_setroot(pu, pn_root);
343ba4e596bSpooka
344ba4e596bSpooka po_root = puffs_getrootpathobj(pu);
345ba4e596bSpooka po_root->po_path = &sname_root;
346ba4e596bSpooka po_root->po_len = 0;
347ba4e596bSpooka
34862234858Spooka fileuid = geteuid();
34962234858Spooka filegid = getegid();
35062234858Spooka
35162234858Spooka if (fileuid == 0)
352b8d16c7dSchristos fileperms = 0644;
35362234858Spooka else
354b8d16c7dSchristos fileperms = 0444;
35562234858Spooka
356ba4e596bSpooka return 0;
357ba4e596bSpooka }
358ba4e596bSpooka
359ba4e596bSpooka int
sysctlfs_fs_fhtonode(struct puffs_usermount * pu,void * fid,size_t fidsize,struct puffs_newinfo * pni)36021913eabSpooka sysctlfs_fs_fhtonode(struct puffs_usermount *pu, void *fid, size_t fidsize,
361ba4e596bSpooka struct puffs_newinfo *pni)
362ba4e596bSpooka {
363ba4e596bSpooka struct puffs_pathobj po;
364ba4e596bSpooka struct puffs_node *pn;
365ba4e596bSpooka struct sfsnode *sfs;
366ba4e596bSpooka struct sfsfid *sfid;
367ba4e596bSpooka
368ba4e596bSpooka sfid = fid;
369ba4e596bSpooka
370ba4e596bSpooka po.po_len = sfid->len;
371ba4e596bSpooka po.po_path = &sfid->path;
372ba4e596bSpooka
37321913eabSpooka pn = getnode(pu, &po, 0);
374ba4e596bSpooka if (pn == NULL)
375ba4e596bSpooka return EINVAL;
376ba4e596bSpooka sfs = pn->pn_data;
377ba4e596bSpooka
378ba4e596bSpooka puffs_newinfo_setcookie(pni, pn);
379ba4e596bSpooka if (ISADIR(sfs))
380ba4e596bSpooka puffs_newinfo_setvtype(pni, VDIR);
381ba4e596bSpooka else
382ba4e596bSpooka puffs_newinfo_setvtype(pni, VREG);
383ba4e596bSpooka
384ba4e596bSpooka return 0;
385ba4e596bSpooka }
386ba4e596bSpooka
387ba4e596bSpooka int
sysctlfs_fs_nodetofh(struct puffs_usermount * pu,void * cookie,void * fid,size_t * fidsize)38821913eabSpooka sysctlfs_fs_nodetofh(struct puffs_usermount *pu, void *cookie,
389ba4e596bSpooka void *fid, size_t *fidsize)
390ba4e596bSpooka {
391ba4e596bSpooka struct puffs_node *pn = cookie;
392ba4e596bSpooka struct sfsfid *sfid;
393ba4e596bSpooka
394ba4e596bSpooka sfid = fid;
395ba4e596bSpooka sfid->len = PNPLEN(pn);
396ba4e596bSpooka memcpy(&sfid->path, PNPATH(pn), sfid->len * sizeof(int));
397ba4e596bSpooka
398ba4e596bSpooka return 0;
399ba4e596bSpooka }
400ba4e596bSpooka
401ba4e596bSpooka static void
getnodedata(struct sfsnode * sfs,struct puffs_pathobj * po,char * buf,size_t * bufsize)40204c15978Spooka getnodedata(struct sfsnode *sfs, struct puffs_pathobj *po,
40304c15978Spooka char *buf, size_t *bufsize)
404ba4e596bSpooka {
405ba4e596bSpooka size_t sz;
40604c15978Spooka int error = 0;
407ba4e596bSpooka
408ba4e596bSpooka assert(!ISADIR(sfs));
409ba4e596bSpooka
41004c15978Spooka memset(buf, 0, *bufsize);
411ba4e596bSpooka switch (SYSCTL_TYPE(sfs->sysctl_flags)) {
4120a80f657Spooka case CTLTYPE_BOOL: {
4130a80f657Spooka bool b;
4140a80f657Spooka sz = sizeof(bool);
4150a80f657Spooka assert(sz <= *bufsize);
4160a80f657Spooka if (sysctl(po->po_path, po->po_len, &b, &sz, NULL, 0) == -1) {
4170a80f657Spooka error = errno;
4180a80f657Spooka break;
4190a80f657Spooka }
4200a80f657Spooka if (rflag)
4210a80f657Spooka memcpy(buf, &b, sz);
4220a80f657Spooka else
4230a80f657Spooka snprintf(buf, *bufsize, "%s", b ? "true" : "false");
4240a80f657Spooka break;
4250a80f657Spooka }
426ba4e596bSpooka case CTLTYPE_INT: {
427ba4e596bSpooka int i;
428ba4e596bSpooka sz = sizeof(int);
42904c15978Spooka assert(sz <= *bufsize);
43004c15978Spooka if (sysctl(po->po_path, po->po_len, &i, &sz, NULL, 0) == -1) {
43104c15978Spooka error = errno;
432ba4e596bSpooka break;
43304c15978Spooka }
43404c15978Spooka if (rflag)
43504c15978Spooka memcpy(buf, &i, sz);
43604c15978Spooka else
43704c15978Spooka snprintf(buf, *bufsize, "%d", i);
438ba4e596bSpooka break;
439ba4e596bSpooka }
440ba4e596bSpooka case CTLTYPE_QUAD: {
441ba4e596bSpooka quad_t q;
442ba4e596bSpooka sz = sizeof(q);
44304c15978Spooka assert(sz <= *bufsize);
44404c15978Spooka if (sysctl(po->po_path, po->po_len, &q, &sz, NULL, 0) == -1) {
44504c15978Spooka error = errno;
446ba4e596bSpooka break;
44704c15978Spooka }
44804c15978Spooka if (rflag)
44904c15978Spooka memcpy(buf, &q, sz);
45004c15978Spooka else
45104c15978Spooka snprintf(buf, *bufsize, "%" PRId64, q);
452ba4e596bSpooka break;
453ba4e596bSpooka }
454c8360f5cSpooka case CTLTYPE_STRUCT: {
455c8360f5cSpooka uint8_t snode[SFS_MAXFILE/2-1];
456c8360f5cSpooka unsigned i;
457c8360f5cSpooka
458c8360f5cSpooka sz = sizeof(snode);
45904c15978Spooka assert(sz <= *bufsize);
46004c15978Spooka if (sysctl(po->po_path, po->po_len, snode, &sz, NULL, 0) == -1){
46104c15978Spooka error = errno;
462ba4e596bSpooka break;
46304c15978Spooka }
46404c15978Spooka if (rflag) {
46504c15978Spooka memcpy(buf, &snode, sz);
46604c15978Spooka } else {
46704c15978Spooka for (i = 0; i < sz && 2*i < *bufsize; i++) {
468c8360f5cSpooka sprintf(&buf[2*i], "%02x", snode[i]);
469c8360f5cSpooka }
470c8360f5cSpooka buf[2*i] = '\0';
47104c15978Spooka }
472c8360f5cSpooka break;
473c8360f5cSpooka }
474ba4e596bSpooka case CTLTYPE_STRING: {
47504c15978Spooka sz = *bufsize;
47604c15978Spooka assert(sz <= *bufsize);
47704c15978Spooka if (sysctl(po->po_path, po->po_len, buf, &sz, NULL, 0) == -1) {
47804c15978Spooka error = errno;
479ba4e596bSpooka break;
48004c15978Spooka }
481ba4e596bSpooka break;
482ba4e596bSpooka }
483ba4e596bSpooka default:
4840a80f657Spooka snprintf(buf, *bufsize, "invalid sysctl CTLTYPE %d",
4850a80f657Spooka SYSCTL_TYPE(sfs->sysctl_flags));
486ba4e596bSpooka break;
487ba4e596bSpooka }
48804c15978Spooka
48904c15978Spooka if (error) {
49004c15978Spooka *bufsize = 0;
49104c15978Spooka return;
49204c15978Spooka }
49304c15978Spooka
49404c15978Spooka if (rflag)
49504c15978Spooka *bufsize = sz;
49604c15978Spooka else
49704c15978Spooka *bufsize = strlen(buf);
498ba4e596bSpooka }
499ba4e596bSpooka
500ba4e596bSpooka static int
getlinks(struct sfsnode * sfs,struct puffs_pathobj * po)501ba4e596bSpooka getlinks(struct sfsnode *sfs, struct puffs_pathobj *po)
502ba4e596bSpooka {
503ba4e596bSpooka struct sysctlnode sn[SFS_NODEPERDIR];
504ba4e596bSpooka struct sysctlnode qnode;
505ba4e596bSpooka SfsName *sname;
506ba4e596bSpooka size_t sl;
507ba4e596bSpooka
508ba4e596bSpooka if (!ISADIR(sfs))
509ba4e596bSpooka return 1;
510ba4e596bSpooka
511ba4e596bSpooka memset(&qnode, 0, sizeof(qnode));
512ba4e596bSpooka sl = sizeof(sn);
513ba4e596bSpooka qnode.sysctl_flags = SYSCTL_VERSION;
514ba4e596bSpooka sname = po->po_path;
515ba4e596bSpooka (*sname)[po->po_len] = CTL_QUERY;
516ba4e596bSpooka
517ba4e596bSpooka if (sysctl(*sname, po->po_len + 1, sn, &sl,
518ba4e596bSpooka &qnode, sizeof(qnode)) == -1)
519ba4e596bSpooka return 0;
520ba4e596bSpooka
521ba4e596bSpooka return (sl / sizeof(sn[0])) + 2;
522ba4e596bSpooka }
523ba4e596bSpooka
524ba4e596bSpooka static int
getsize(struct sfsnode * sfs,struct puffs_pathobj * po)525ba4e596bSpooka getsize(struct sfsnode *sfs, struct puffs_pathobj *po)
526ba4e596bSpooka {
527ba4e596bSpooka char buf[SFS_MAXFILE];
52804c15978Spooka size_t sz = sizeof(buf);
529ba4e596bSpooka
530ba4e596bSpooka if (ISADIR(sfs))
531ba4e596bSpooka return getlinks(sfs, po) * 16; /* totally arbitrary */
532ba4e596bSpooka
53304c15978Spooka getnodedata(sfs, po, buf, &sz);
53404c15978Spooka if (rflag)
53504c15978Spooka return sz;
53604c15978Spooka else
53704c15978Spooka return sz + 1; /* for \n, not \0 */
538ba4e596bSpooka }
539ba4e596bSpooka
540ba4e596bSpooka int
sysctlfs_node_lookup(struct puffs_usermount * pu,void * opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn)54121913eabSpooka sysctlfs_node_lookup(struct puffs_usermount *pu, void *opc,
54221913eabSpooka struct puffs_newinfo *pni, const struct puffs_cn *pcn)
543ba4e596bSpooka {
544ba4e596bSpooka struct puffs_cn *p2cn = __UNCONST(pcn); /* XXX: fix the interface */
545ba4e596bSpooka struct sysctlnode sn[SFS_NODEPERDIR];
546ba4e596bSpooka struct sysctlnode qnode;
547ba4e596bSpooka struct puffs_node *pn_dir = opc;
548ba4e596bSpooka struct puffs_node *pn_new;
549ba4e596bSpooka struct sfsnode *sfs_dir = pn_dir->pn_data, *sfs_new;
550ba4e596bSpooka SfsName *sname = PCNPATH(pcn);
5513cafe960Slukem size_t sl, i;
5523cafe960Slukem int nodetype;
553ba4e596bSpooka
554ba4e596bSpooka assert(ISADIR(sfs_dir));
555ba4e596bSpooka
556ba4e596bSpooka /*
557ba4e596bSpooka * If we're looking for dotdot, we already have the entire pathname
558ba4e596bSpooka * in sname, courtesy of pathbuild, so we can skip this step.
559ba4e596bSpooka */
560ba4e596bSpooka if (!PCNISDOTDOT(pcn)) {
561ba4e596bSpooka memset(&qnode, 0, sizeof(qnode));
562ba4e596bSpooka sl = SFS_NODEPERDIR * sizeof(struct sysctlnode);
563ba4e596bSpooka qnode.sysctl_flags = SYSCTL_VERSION;
564ba4e596bSpooka (*sname)[PCNPLEN(pcn)] = CTL_QUERY;
565ba4e596bSpooka
566ba4e596bSpooka if (sysctl(*sname, PCNPLEN(pcn) + 1, sn, &sl,
567ba4e596bSpooka &qnode, sizeof(qnode)) == -1)
568ba4e596bSpooka return ENOENT;
569ba4e596bSpooka
570ba4e596bSpooka for (i = 0; i < sl / sizeof(struct sysctlnode); i++)
571ba4e596bSpooka if (strcmp(sn[i].sysctl_name, pcn->pcn_name) == 0)
572ba4e596bSpooka break;
573ba4e596bSpooka if (i == sl / sizeof(struct sysctlnode))
574ba4e596bSpooka return ENOENT;
575ba4e596bSpooka
576ba4e596bSpooka (*sname)[PCNPLEN(pcn)] = sn[i].sysctl_num;
577ba4e596bSpooka p2cn->pcn_po_full.po_len++;
578ba4e596bSpooka nodetype = sn[i].sysctl_flags;
579ba4e596bSpooka } else
580ba4e596bSpooka nodetype = CTLTYPE_NODE;
581ba4e596bSpooka
582ba4e596bSpooka pn_new = getnode(pu, &p2cn->pcn_po_full, nodetype);
58353500167Schristos if (pn_new == NULL)
58453500167Schristos return ENOENT;
585ba4e596bSpooka sfs_new = pn_new->pn_data;
586ba4e596bSpooka
587ba4e596bSpooka puffs_newinfo_setcookie(pni, pn_new);
588ba4e596bSpooka if (ISADIR(sfs_new))
589ba4e596bSpooka puffs_newinfo_setvtype(pni, VDIR);
590ba4e596bSpooka else
591ba4e596bSpooka puffs_newinfo_setvtype(pni, VREG);
592ba4e596bSpooka
593ba4e596bSpooka return 0;
594ba4e596bSpooka }
595ba4e596bSpooka
596ba4e596bSpooka int
sysctlfs_node_getattr(struct puffs_usermount * pu,void * opc,struct vattr * va,const struct puffs_cred * pcr)59721913eabSpooka sysctlfs_node_getattr(struct puffs_usermount *pu, void *opc, struct vattr *va,
5986a3d9a18Spooka const struct puffs_cred *pcr)
599ba4e596bSpooka {
600ba4e596bSpooka struct puffs_node *pn = opc;
601ba4e596bSpooka struct sfsnode *sfs = pn->pn_data;
602ba4e596bSpooka
603ba4e596bSpooka memset(va, 0, sizeof(struct vattr));
604ba4e596bSpooka
605ba4e596bSpooka if (ISADIR(sfs)) {
606ba4e596bSpooka va->va_type = VDIR;
60762234858Spooka va->va_mode = 0555;
608ba4e596bSpooka } else {
609ba4e596bSpooka va->va_type = VREG;
61062234858Spooka va->va_mode = fileperms;
611ba4e596bSpooka }
61262234858Spooka va->va_uid = fileuid;
61362234858Spooka va->va_gid = filegid;
614ba4e596bSpooka va->va_nlink = getlinks(sfs, &pn->pn_po);
615ba4e596bSpooka va->va_fileid = sfs->myid;
616ba4e596bSpooka va->va_size = getsize(sfs, &pn->pn_po);
617ba4e596bSpooka va->va_gen = 1;
618ba4e596bSpooka va->va_rdev = PUFFS_VNOVAL;
619ba4e596bSpooka va->va_blocksize = 512;
620ba4e596bSpooka va->va_filerev = 1;
621ba4e596bSpooka
622ba4e596bSpooka va->va_atime = va->va_mtime = va->va_ctime = va->va_birthtime = fstime;
623ba4e596bSpooka
624ba4e596bSpooka return 0;
625ba4e596bSpooka }
626ba4e596bSpooka
627ba4e596bSpooka int
sysctlfs_node_setattr(struct puffs_usermount * pu,void * opc,const struct vattr * va,const struct puffs_cred * pcr)62821913eabSpooka sysctlfs_node_setattr(struct puffs_usermount *pu, void *opc,
6296a3d9a18Spooka const struct vattr *va, const struct puffs_cred *pcr)
630ba4e596bSpooka {
631ba4e596bSpooka
632ba4e596bSpooka /* dummy, but required for write */
63362234858Spooka /* XXX: we could return EOPNOTSUPP or something */
634ba4e596bSpooka return 0;
635ba4e596bSpooka }
636ba4e596bSpooka
637ba4e596bSpooka int
sysctlfs_node_readdir(struct puffs_usermount * pu,void * opc,struct dirent * dent,off_t * readoff,size_t * reslen,const struct puffs_cred * pcr,int * eofflag,off_t * cookies,size_t * ncookies)63821913eabSpooka sysctlfs_node_readdir(struct puffs_usermount *pu, void *opc,
63921913eabSpooka struct dirent *dent, off_t *readoff, size_t *reslen,
64021913eabSpooka const struct puffs_cred *pcr, int *eofflag,
64121913eabSpooka off_t *cookies, size_t *ncookies)
642ba4e596bSpooka {
643ba4e596bSpooka struct sysctlnode sn[SFS_NODEPERDIR];
644ba4e596bSpooka struct sysctlnode qnode;
645ba4e596bSpooka struct puffs_node *pn_dir = opc;
646ba4e596bSpooka struct puffs_node *pn_res;
647ba4e596bSpooka struct puffs_pathobj po;
648ba4e596bSpooka struct sfsnode *sfs_dir = pn_dir->pn_data, *sfs_ent;
649ba4e596bSpooka SfsName *sname;
6503cafe960Slukem size_t sl, i;
651ba4e596bSpooka enum vtype vt;
652ba4e596bSpooka ino_t id;
653ba4e596bSpooka
654ba4e596bSpooka *ncookies = 0;
655ba4e596bSpooka
656ba4e596bSpooka again:
657ba4e596bSpooka if (*readoff == DENT_DOT || *readoff == DENT_DOTDOT) {
658ba4e596bSpooka puffs_gendotdent(&dent, sfs_dir->myid, *readoff, reslen);
659ba4e596bSpooka (*readoff)++;
660ba4e596bSpooka PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff);
661ba4e596bSpooka goto again;
662ba4e596bSpooka }
663ba4e596bSpooka
664ba4e596bSpooka memset(&qnode, 0, sizeof(qnode));
665ba4e596bSpooka sl = SFS_NODEPERDIR * sizeof(struct sysctlnode);
666ba4e596bSpooka qnode.sysctl_flags = SYSCTL_VERSION;
667ba4e596bSpooka sname = PNPATH(pn_dir);
668ba4e596bSpooka (*sname)[PNPLEN(pn_dir)] = CTL_QUERY;
669ba4e596bSpooka
670ba4e596bSpooka if (sysctl(*sname, PNPLEN(pn_dir) + 1, sn, &sl,
671ba4e596bSpooka &qnode, sizeof(qnode)) == -1)
672ba4e596bSpooka return ENOENT;
673ba4e596bSpooka
674ba4e596bSpooka po.po_path = sname;
675ba4e596bSpooka po.po_len = PNPLEN(pn_dir)+1;
676ba4e596bSpooka
677ba4e596bSpooka for (i = DENT_ADJ(*readoff); i < sl / sizeof(struct sysctlnode); i++) {
678ba4e596bSpooka if (SYSCTL_TYPE(sn[i].sysctl_flags) == CTLTYPE_NODE)
679ba4e596bSpooka vt = VDIR;
680ba4e596bSpooka else
681ba4e596bSpooka vt = VREG;
682ba4e596bSpooka
683ba4e596bSpooka /*
684ba4e596bSpooka * check if the node exists. if so, give it the real
685ba4e596bSpooka * inode number. otherwise just fake it.
686ba4e596bSpooka */
687ba4e596bSpooka (*sname)[PNPLEN(pn_dir)] = sn[i].sysctl_num;
688ba4e596bSpooka pn_res = puffs_pn_nodewalk(pu, puffs_path_walkcmp, &po);
689ba4e596bSpooka if (pn_res) {
690ba4e596bSpooka sfs_ent = pn_res->pn_data;
691ba4e596bSpooka id = sfs_ent->myid;
692ba4e596bSpooka } else {
693ba4e596bSpooka id = nextid++;
694ba4e596bSpooka }
695ba4e596bSpooka
696ba4e596bSpooka if (!puffs_nextdent(&dent, sn[i].sysctl_name, id,
697ba4e596bSpooka puffs_vtype2dt(vt), reslen))
698ba4e596bSpooka return 0;
699ba4e596bSpooka
700ba4e596bSpooka (*readoff)++;
701ba4e596bSpooka PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff);
702ba4e596bSpooka }
703ba4e596bSpooka
704ba4e596bSpooka *eofflag = 1;
705ba4e596bSpooka return 0;
706ba4e596bSpooka }
707ba4e596bSpooka
708ba4e596bSpooka int
sysctlfs_node_read(struct puffs_usermount * pu,void * opc,uint8_t * buf,off_t offset,size_t * resid,const struct puffs_cred * pcr,int ioflag)70921913eabSpooka sysctlfs_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf,
710ba4e596bSpooka off_t offset, size_t *resid, const struct puffs_cred *pcr,
711ba4e596bSpooka int ioflag)
712ba4e596bSpooka {
713ba4e596bSpooka char localbuf[SFS_MAXFILE];
714ba4e596bSpooka struct puffs_node *pn = opc;
715ba4e596bSpooka struct sfsnode *sfs = pn->pn_data;
71604c15978Spooka size_t sz = sizeof(localbuf);
717ba4e596bSpooka int xfer;
718ba4e596bSpooka
719ba4e596bSpooka if (ISADIR(sfs))
720ba4e596bSpooka return EISDIR;
721ba4e596bSpooka
72204c15978Spooka getnodedata(sfs, &pn->pn_po, localbuf, &sz);
72304c15978Spooka if ((ssize_t)sz < offset)
7244c9ac951Snjoly xfer = 0;
7254c9ac951Snjoly else
72604c15978Spooka xfer = MIN(*resid, sz - offset);
727ba4e596bSpooka
728ba4e596bSpooka if (xfer <= 0)
729ba4e596bSpooka return 0;
730ba4e596bSpooka
731ba4e596bSpooka memcpy(buf, localbuf + offset, xfer);
732ba4e596bSpooka *resid -= xfer;
733ba4e596bSpooka
73404c15978Spooka if (*resid && !rflag) {
735ba4e596bSpooka buf[xfer] = '\n';
736ba4e596bSpooka (*resid)--;
737ba4e596bSpooka }
738ba4e596bSpooka
739ba4e596bSpooka return 0;
740ba4e596bSpooka }
741ba4e596bSpooka
742ba4e596bSpooka int
sysctlfs_node_write(struct puffs_usermount * pu,void * opc,uint8_t * buf,off_t offset,size_t * resid,const struct puffs_cred * cred,int ioflag)74321913eabSpooka sysctlfs_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf,
744ba4e596bSpooka off_t offset, size_t *resid, const struct puffs_cred *cred,
745ba4e596bSpooka int ioflag)
746ba4e596bSpooka {
747ba4e596bSpooka struct puffs_node *pn = opc;
748ba4e596bSpooka struct sfsnode *sfs = pn->pn_data;
749ba4e596bSpooka long long ll;
750ba4e596bSpooka int i, rv;
7510a80f657Spooka bool b;
752ba4e596bSpooka
75304c15978Spooka /*
75404c15978Spooka * I picked the wrong day to ... um, the wrong place to return errors
75504c15978Spooka */
75604c15978Spooka
75704c15978Spooka /* easy to support, but just unavailable now */
75804c15978Spooka if (rflag)
75904c15978Spooka return EOPNOTSUPP;
76004c15978Spooka
761ba4e596bSpooka if (puffs_cred_isjuggernaut(cred) == 0)
762ba4e596bSpooka return EACCES;
763ba4e596bSpooka
764ba4e596bSpooka if (ISADIR(sfs))
765ba4e596bSpooka return EISDIR;
766ba4e596bSpooka
767ba4e596bSpooka if (offset != 0)
768ba4e596bSpooka return EINVAL;
769ba4e596bSpooka
770ba4e596bSpooka if (ioflag & PUFFS_IO_APPEND)
771ba4e596bSpooka return EINVAL;
772ba4e596bSpooka
773ba4e596bSpooka switch (SYSCTL_TYPE(sfs->sysctl_flags)) {
7740a80f657Spooka case CTLTYPE_BOOL:
7750a80f657Spooka if (strcasestr((const char *)buf, "true"))
7760a80f657Spooka b = true;
7770a80f657Spooka else if (strcasestr((const char *)buf, "false"))
7780a80f657Spooka b = false;
7790a80f657Spooka else
7800a80f657Spooka return EINVAL;
7810a80f657Spooka rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL,
7820a80f657Spooka &b, sizeof(b));
7830a80f657Spooka break;
784ba4e596bSpooka case CTLTYPE_INT:
785ba4e596bSpooka if (sscanf((const char *)buf, "%d", &i) != 1)
786ba4e596bSpooka return EINVAL;
787ba4e596bSpooka rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL,
788ba4e596bSpooka &i, sizeof(int));
789ba4e596bSpooka break;
790ba4e596bSpooka case CTLTYPE_QUAD:
791ba4e596bSpooka if (sscanf((const char *)buf, "%lld", &ll) != 1)
792ba4e596bSpooka return EINVAL;
793ba4e596bSpooka rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL,
794ba4e596bSpooka &ll, sizeof(long long));
795ba4e596bSpooka break;
796ba4e596bSpooka case CTLTYPE_STRING:
797ba4e596bSpooka rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL, buf, *resid);
798ba4e596bSpooka break;
799ba4e596bSpooka default:
800ba4e596bSpooka rv = EINVAL;
801ba4e596bSpooka break;
802ba4e596bSpooka }
803ba4e596bSpooka
804ba4e596bSpooka if (rv)
805ba4e596bSpooka return rv;
806ba4e596bSpooka
807ba4e596bSpooka *resid = 0;
808ba4e596bSpooka return 0;
809ba4e596bSpooka }
810