xref: /minix3/minix/servers/mib/tree.c (revision e4e21ee1b2710f3d411514741ce10d80156f4b5d)
1*e4e21ee1SDavid van Moolenbroek /* MIB service - tree.c - tree access and management */
2*e4e21ee1SDavid van Moolenbroek 
3*e4e21ee1SDavid van Moolenbroek #include "mib.h"
4*e4e21ee1SDavid van Moolenbroek 
5*e4e21ee1SDavid van Moolenbroek /*
6*e4e21ee1SDavid van Moolenbroek  * Does the given identifier fall within the range of static identifiers in the
7*e4e21ee1SDavid van Moolenbroek  * given parent?  This check can be used to enumerate all static array entries
8*e4e21ee1SDavid van Moolenbroek  * in the given parent, starting from zero.  The check does not guarantee that
9*e4e21ee1SDavid van Moolenbroek  * the entry is actually for a valid node, nor does it guarantee that there is
10*e4e21ee1SDavid van Moolenbroek  * not a dynamic node with this identifier.
11*e4e21ee1SDavid van Moolenbroek  */
12*e4e21ee1SDavid van Moolenbroek #define IS_STATIC_ID(parent, id) ((unsigned int)(id) < (parent)->node_size)
13*e4e21ee1SDavid van Moolenbroek 
14*e4e21ee1SDavid van Moolenbroek /*
15*e4e21ee1SDavid van Moolenbroek  * Scratch buffer, used for various cases of temporary data storage.  It must
16*e4e21ee1SDavid van Moolenbroek  * be large enough to fit a sysctldesc structure followed by the longest
17*e4e21ee1SDavid van Moolenbroek  * supported description.  It must also be large enough to serve as temporary
18*e4e21ee1SDavid van Moolenbroek  * storage for data being written in the majority of cases.  Finally, it must
19*e4e21ee1SDavid van Moolenbroek  * be large enough to contain an entire page, for mib_copyin_str().
20*e4e21ee1SDavid van Moolenbroek  */
21*e4e21ee1SDavid van Moolenbroek #define MAXDESCLEN	1024	/* from NetBSD */
22*e4e21ee1SDavid van Moolenbroek #define SCRATCH_SIZE	MAX(PAGE_SIZE, sizeof(struct sysctldesc) + MAXDESCLEN)
23*e4e21ee1SDavid van Moolenbroek static char scratch[SCRATCH_SIZE] __aligned(sizeof(int32_t));
24*e4e21ee1SDavid van Moolenbroek 
25*e4e21ee1SDavid van Moolenbroek unsigned int nodes;	/* how many nodes are there in the tree? */
26*e4e21ee1SDavid van Moolenbroek unsigned int objects;	/* how many allocated memory objects are there? */
27*e4e21ee1SDavid van Moolenbroek 
28*e4e21ee1SDavid van Moolenbroek /*
29*e4e21ee1SDavid van Moolenbroek  * Find a node through its parent node and identifier.  Return the node if it
30*e4e21ee1SDavid van Moolenbroek  * was found, and optionally store a pointer to the pointer to its dynode
31*e4e21ee1SDavid van Moolenbroek  * superstructure (for removal).  If no matching node was found, return NULL.
32*e4e21ee1SDavid van Moolenbroek  */
33*e4e21ee1SDavid van Moolenbroek static struct mib_node *
34*e4e21ee1SDavid van Moolenbroek mib_find(struct mib_node * parent, int id, struct mib_dynode *** prevpp)
35*e4e21ee1SDavid van Moolenbroek {
36*e4e21ee1SDavid van Moolenbroek 	struct mib_node *node;
37*e4e21ee1SDavid van Moolenbroek 	struct mib_dynode **dynp;
38*e4e21ee1SDavid van Moolenbroek 
39*e4e21ee1SDavid van Moolenbroek 	if (id < 0)
40*e4e21ee1SDavid van Moolenbroek 		return NULL;
41*e4e21ee1SDavid van Moolenbroek 
42*e4e21ee1SDavid van Moolenbroek 	/*
43*e4e21ee1SDavid van Moolenbroek 	 * Is there a static node with this identifier?  The static nodes are
44*e4e21ee1SDavid van Moolenbroek 	 * all in a single array, so lookup is O(1) for these nodes.  We use
45*e4e21ee1SDavid van Moolenbroek 	 * the node flags field to see whether the array entry is valid.
46*e4e21ee1SDavid van Moolenbroek 	 */
47*e4e21ee1SDavid van Moolenbroek 	if (IS_STATIC_ID(parent, id)) {
48*e4e21ee1SDavid van Moolenbroek 		node = &parent->node_scptr[id];
49*e4e21ee1SDavid van Moolenbroek 
50*e4e21ee1SDavid van Moolenbroek 		if (node->node_flags != 0) {
51*e4e21ee1SDavid van Moolenbroek 			/* Found a matching static node. */
52*e4e21ee1SDavid van Moolenbroek 			if (prevpp != NULL)
53*e4e21ee1SDavid van Moolenbroek 				*prevpp = NULL;
54*e4e21ee1SDavid van Moolenbroek 			return node;
55*e4e21ee1SDavid van Moolenbroek 		}
56*e4e21ee1SDavid van Moolenbroek 	}
57*e4e21ee1SDavid van Moolenbroek 
58*e4e21ee1SDavid van Moolenbroek 	/*
59*e4e21ee1SDavid van Moolenbroek 	 * Is there a dynamic node with this identifier?  The dynamic nodes
60*e4e21ee1SDavid van Moolenbroek 	 * form a linked list.  This is predominantly because userland may pick
61*e4e21ee1SDavid van Moolenbroek 	 * the identifier number at creation time, so we cannot rely on all
62*e4e21ee1SDavid van Moolenbroek 	 * dynamically created nodes falling into a small identifier range.
63*e4e21ee1SDavid van Moolenbroek 	 * That eliminates the option of a dynamic array indexed by identifier,
64*e4e21ee1SDavid van Moolenbroek 	 * and a linked list is the simplest next option.  Thus, dynamic node
65*e4e21ee1SDavid van Moolenbroek 	 * lookup is O(n).  However, since the list is sorted by identifier,
66*e4e21ee1SDavid van Moolenbroek 	 * we may be able to stop the search early.
67*e4e21ee1SDavid van Moolenbroek 	 */
68*e4e21ee1SDavid van Moolenbroek 	for (dynp = &parent->node_dcptr; *dynp != NULL;
69*e4e21ee1SDavid van Moolenbroek 	    dynp = &((*dynp)->dynode_next)) {
70*e4e21ee1SDavid van Moolenbroek 		if ((*dynp)->dynode_id == id) {
71*e4e21ee1SDavid van Moolenbroek 			/* Found a matching dynamic node. */
72*e4e21ee1SDavid van Moolenbroek 			if (prevpp != NULL)
73*e4e21ee1SDavid van Moolenbroek 				*prevpp = dynp;
74*e4e21ee1SDavid van Moolenbroek 			return &(*dynp)->dynode_node;
75*e4e21ee1SDavid van Moolenbroek 		} else if ((*dynp)->dynode_id > id)
76*e4e21ee1SDavid van Moolenbroek 			break; /* no need to look further */
77*e4e21ee1SDavid van Moolenbroek 	}
78*e4e21ee1SDavid van Moolenbroek 
79*e4e21ee1SDavid van Moolenbroek 	return NULL;
80*e4e21ee1SDavid van Moolenbroek }
81*e4e21ee1SDavid van Moolenbroek 
82*e4e21ee1SDavid van Moolenbroek /*
83*e4e21ee1SDavid van Moolenbroek  * Copy out a node to userland, using the exchange format for nodes (namely,
84*e4e21ee1SDavid van Moolenbroek  * a sysctlnode structure).  Return the size of the object that is (or, if the
85*e4e21ee1SDavid van Moolenbroek  * node falls outside the requested data range, would be) copied out on
86*e4e21ee1SDavid van Moolenbroek  * success, or a negative error code on failure.  The function may return 0
87*e4e21ee1SDavid van Moolenbroek  * to indicate that nothing was copied out after all (this is unused here).
88*e4e21ee1SDavid van Moolenbroek  */
89*e4e21ee1SDavid van Moolenbroek static ssize_t
90*e4e21ee1SDavid van Moolenbroek mib_copyout_node(struct mib_call * call, struct mib_oldp * oldp, size_t off,
91*e4e21ee1SDavid van Moolenbroek 	int id, const struct mib_node * node)
92*e4e21ee1SDavid van Moolenbroek {
93*e4e21ee1SDavid van Moolenbroek 	struct sysctlnode scn;
94*e4e21ee1SDavid van Moolenbroek 	int visible;
95*e4e21ee1SDavid van Moolenbroek 
96*e4e21ee1SDavid van Moolenbroek 	if (!mib_inrange(oldp, off))
97*e4e21ee1SDavid van Moolenbroek 		return sizeof(scn); /* nothing to do */
98*e4e21ee1SDavid van Moolenbroek 
99*e4e21ee1SDavid van Moolenbroek 	memset(&scn, 0, sizeof(scn));
100*e4e21ee1SDavid van Moolenbroek 
101*e4e21ee1SDavid van Moolenbroek 	/*
102*e4e21ee1SDavid van Moolenbroek 	 * We use CTLFLAG_PARENT and CTLFLAG_VERIFY internally only.  NetBSD
103*e4e21ee1SDavid van Moolenbroek 	 * uses the values of these flags for different purposes.  Either way,
104*e4e21ee1SDavid van Moolenbroek 	 * do not expose them to userland.
105*e4e21ee1SDavid van Moolenbroek 	 */
106*e4e21ee1SDavid van Moolenbroek 	scn.sysctl_flags = SYSCTL_VERSION |
107*e4e21ee1SDavid van Moolenbroek 	    (node->node_flags & ~(CTLFLAG_PARENT | CTLFLAG_VERIFY));
108*e4e21ee1SDavid van Moolenbroek 	scn.sysctl_num = id;
109*e4e21ee1SDavid van Moolenbroek 	strlcpy(scn.sysctl_name, node->node_name, sizeof(scn.sysctl_name));
110*e4e21ee1SDavid van Moolenbroek 	scn.sysctl_ver = node->node_ver;
111*e4e21ee1SDavid van Moolenbroek 	scn.sysctl_size = node->node_size;
112*e4e21ee1SDavid van Moolenbroek 
113*e4e21ee1SDavid van Moolenbroek 	/* Some information is only visible if the user can access the node. */
114*e4e21ee1SDavid van Moolenbroek 	visible = (!(node->node_flags & CTLFLAG_PRIVATE) || mib_authed(call));
115*e4e21ee1SDavid van Moolenbroek 
116*e4e21ee1SDavid van Moolenbroek 	/*
117*e4e21ee1SDavid van Moolenbroek 	 * For immediate types, store the immediate value in the resulting
118*e4e21ee1SDavid van Moolenbroek 	 * structure, unless the caller is not authorized to obtain the value.
119*e4e21ee1SDavid van Moolenbroek 	 */
120*e4e21ee1SDavid van Moolenbroek 	if ((node->node_flags & CTLFLAG_IMMEDIATE) && visible) {
121*e4e21ee1SDavid van Moolenbroek 		switch (SYSCTL_TYPE(node->node_flags)) {
122*e4e21ee1SDavid van Moolenbroek 		case CTLTYPE_BOOL:
123*e4e21ee1SDavid van Moolenbroek 			scn.sysctl_bdata = node->node_bool;
124*e4e21ee1SDavid van Moolenbroek 			break;
125*e4e21ee1SDavid van Moolenbroek 		case CTLTYPE_INT:
126*e4e21ee1SDavid van Moolenbroek 			scn.sysctl_idata = node->node_int;
127*e4e21ee1SDavid van Moolenbroek 			break;
128*e4e21ee1SDavid van Moolenbroek 		case CTLTYPE_QUAD:
129*e4e21ee1SDavid van Moolenbroek 			scn.sysctl_qdata = node->node_quad;
130*e4e21ee1SDavid van Moolenbroek 		}
131*e4e21ee1SDavid van Moolenbroek 	}
132*e4e21ee1SDavid van Moolenbroek 
133*e4e21ee1SDavid van Moolenbroek 	/* Special rules apply to parent nodes. */
134*e4e21ee1SDavid van Moolenbroek 	if (SYSCTL_TYPE(node->node_flags) == CTLTYPE_NODE) {
135*e4e21ee1SDavid van Moolenbroek 		/* Report the node size the way NetBSD does, just in case. */
136*e4e21ee1SDavid van Moolenbroek 		scn.sysctl_size = sizeof(scn);
137*e4e21ee1SDavid van Moolenbroek 
138*e4e21ee1SDavid van Moolenbroek 		/* If this is a real parent node, report child information. */
139*e4e21ee1SDavid van Moolenbroek 		if ((node->node_flags & CTLFLAG_PARENT) && visible) {
140*e4e21ee1SDavid van Moolenbroek 			scn.sysctl_csize = node->node_csize;
141*e4e21ee1SDavid van Moolenbroek 			scn.sysctl_clen = node->node_clen;
142*e4e21ee1SDavid van Moolenbroek 		}
143*e4e21ee1SDavid van Moolenbroek 
144*e4e21ee1SDavid van Moolenbroek 		/*
145*e4e21ee1SDavid van Moolenbroek 		 * If this is a function-driven node, indicate this by setting
146*e4e21ee1SDavid van Moolenbroek 		 * a nonzero function address.  This allows trace(1) to
147*e4e21ee1SDavid van Moolenbroek 		 * determine that it should not attempt to descend into this
148*e4e21ee1SDavid van Moolenbroek 		 * part of the tree as usual, because a) accessing subnodes may
149*e4e21ee1SDavid van Moolenbroek 		 * have side effects, and b) meta-identifiers may not work as
150*e4e21ee1SDavid van Moolenbroek 		 * expected in these parts of the tree.  Do not return the real
151*e4e21ee1SDavid van Moolenbroek 		 * function pointer, as this would leak anti-ASR information.
152*e4e21ee1SDavid van Moolenbroek 		 */
153*e4e21ee1SDavid van Moolenbroek 		if (!(node->node_flags & CTLFLAG_PARENT))
154*e4e21ee1SDavid van Moolenbroek 			scn.sysctl_func = SYSCTL_NODE_FN;
155*e4e21ee1SDavid van Moolenbroek 	}
156*e4e21ee1SDavid van Moolenbroek 
157*e4e21ee1SDavid van Moolenbroek 	/* Copy out the resulting node. */
158*e4e21ee1SDavid van Moolenbroek 	return mib_copyout(oldp, off, &scn, sizeof(scn));
159*e4e21ee1SDavid van Moolenbroek }
160*e4e21ee1SDavid van Moolenbroek 
161*e4e21ee1SDavid van Moolenbroek /*
162*e4e21ee1SDavid van Moolenbroek  * Given a query on a non-leaf (parent) node, provide the user with an array of
163*e4e21ee1SDavid van Moolenbroek  * this node's children.
164*e4e21ee1SDavid van Moolenbroek  */
165*e4e21ee1SDavid van Moolenbroek static ssize_t
166*e4e21ee1SDavid van Moolenbroek mib_query(struct mib_call * call, struct mib_node * parent,
167*e4e21ee1SDavid van Moolenbroek 	struct mib_oldp * oldp, struct mib_newp * newp, struct mib_node * root)
168*e4e21ee1SDavid van Moolenbroek {
169*e4e21ee1SDavid van Moolenbroek 	struct sysctlnode scn;
170*e4e21ee1SDavid van Moolenbroek 	struct mib_node *node;
171*e4e21ee1SDavid van Moolenbroek 	struct mib_dynode *dynode;
172*e4e21ee1SDavid van Moolenbroek 	size_t off;
173*e4e21ee1SDavid van Moolenbroek 	int r, id;
174*e4e21ee1SDavid van Moolenbroek 
175*e4e21ee1SDavid van Moolenbroek 	/* If the user passed in version numbers, check them. */
176*e4e21ee1SDavid van Moolenbroek 	if (newp != NULL) {
177*e4e21ee1SDavid van Moolenbroek 		if ((r = mib_copyin(newp, &scn, sizeof(scn))) != OK)
178*e4e21ee1SDavid van Moolenbroek 			return r;
179*e4e21ee1SDavid van Moolenbroek 
180*e4e21ee1SDavid van Moolenbroek 		if (SYSCTL_VERS(scn.sysctl_flags) != SYSCTL_VERSION)
181*e4e21ee1SDavid van Moolenbroek 			return EINVAL;
182*e4e21ee1SDavid van Moolenbroek 
183*e4e21ee1SDavid van Moolenbroek 		/*
184*e4e21ee1SDavid van Moolenbroek 		 * If a node version number is given, it must match the version
185*e4e21ee1SDavid van Moolenbroek 		 * of the parent or the root.
186*e4e21ee1SDavid van Moolenbroek 		 */
187*e4e21ee1SDavid van Moolenbroek 		if (scn.sysctl_ver != 0 && scn.sysctl_ver != root->node_ver &&
188*e4e21ee1SDavid van Moolenbroek 		    scn.sysctl_ver != parent->node_ver)
189*e4e21ee1SDavid van Moolenbroek 			return EINVAL;
190*e4e21ee1SDavid van Moolenbroek 	}
191*e4e21ee1SDavid van Moolenbroek 
192*e4e21ee1SDavid van Moolenbroek 	/*
193*e4e21ee1SDavid van Moolenbroek 	 * We need not return the nodes strictly in ascending order of
194*e4e21ee1SDavid van Moolenbroek 	 * identifiers, as this is not expected by userland.  For example,
195*e4e21ee1SDavid van Moolenbroek 	 * sysctlgetmibinfo(3) performs its own sorting after a query.
196*e4e21ee1SDavid van Moolenbroek 	 * Thus, we can go through the static and dynamic nodes separately.
197*e4e21ee1SDavid van Moolenbroek 	 */
198*e4e21ee1SDavid van Moolenbroek 	off = 0;
199*e4e21ee1SDavid van Moolenbroek 
200*e4e21ee1SDavid van Moolenbroek 	/* First enumerate the static nodes. */
201*e4e21ee1SDavid van Moolenbroek 	for (id = 0; IS_STATIC_ID(parent, id); id++) {
202*e4e21ee1SDavid van Moolenbroek 		node = &parent->node_scptr[id];
203*e4e21ee1SDavid van Moolenbroek 
204*e4e21ee1SDavid van Moolenbroek 		if (node->node_flags == 0)
205*e4e21ee1SDavid van Moolenbroek 			continue;
206*e4e21ee1SDavid van Moolenbroek 
207*e4e21ee1SDavid van Moolenbroek 		if ((r = mib_copyout_node(call, oldp, off, id, node)) < 0)
208*e4e21ee1SDavid van Moolenbroek 			return r;
209*e4e21ee1SDavid van Moolenbroek 		off += r;
210*e4e21ee1SDavid van Moolenbroek 	}
211*e4e21ee1SDavid van Moolenbroek 
212*e4e21ee1SDavid van Moolenbroek 	/* Then enumerate the dynamic nodes. */
213*e4e21ee1SDavid van Moolenbroek 	for (dynode = parent->node_dcptr; dynode != NULL;
214*e4e21ee1SDavid van Moolenbroek 	    dynode = dynode->dynode_next) {
215*e4e21ee1SDavid van Moolenbroek 		node = &dynode->dynode_node;
216*e4e21ee1SDavid van Moolenbroek 
217*e4e21ee1SDavid van Moolenbroek 		if ((r = mib_copyout_node(call, oldp, off, dynode->dynode_id,
218*e4e21ee1SDavid van Moolenbroek 		    node)) < 0)
219*e4e21ee1SDavid van Moolenbroek 			return r;
220*e4e21ee1SDavid van Moolenbroek 		off += r;
221*e4e21ee1SDavid van Moolenbroek 	}
222*e4e21ee1SDavid van Moolenbroek 
223*e4e21ee1SDavid van Moolenbroek 	return off;
224*e4e21ee1SDavid van Moolenbroek }
225*e4e21ee1SDavid van Moolenbroek 
226*e4e21ee1SDavid van Moolenbroek /*
227*e4e21ee1SDavid van Moolenbroek  * Scan a parent node's children, as part of new node creation.  Search for
228*e4e21ee1SDavid van Moolenbroek  * either a free node identifier (if given_id < 0) or collisions with the node
229*e4e21ee1SDavid van Moolenbroek  * identifier to use (if given_id >= 0).  Also check for name collisions.  Upon
230*e4e21ee1SDavid van Moolenbroek  * success, return OK, with the resulting node identifier stored in 'idp' and a
231*e4e21ee1SDavid van Moolenbroek  * pointer to the pointer for the new dynamic node stored in 'prevpp'.  Upon
232*e4e21ee1SDavid van Moolenbroek  * failure, return an error code.  If the failure is EEXIST, 'idp' will contain
233*e4e21ee1SDavid van Moolenbroek  * the ID of the conflicting node, and 'nodep' will point to the node.
234*e4e21ee1SDavid van Moolenbroek  */
235*e4e21ee1SDavid van Moolenbroek static int
236*e4e21ee1SDavid van Moolenbroek mib_scan(struct mib_node * parent, int given_id, const char * name, int * idp,
237*e4e21ee1SDavid van Moolenbroek 	struct mib_dynode *** prevpp, struct mib_node ** nodep)
238*e4e21ee1SDavid van Moolenbroek {
239*e4e21ee1SDavid van Moolenbroek 	struct mib_dynode **prevp, **dynp;
240*e4e21ee1SDavid van Moolenbroek 	struct mib_node *node;
241*e4e21ee1SDavid van Moolenbroek 	int id;
242*e4e21ee1SDavid van Moolenbroek 
243*e4e21ee1SDavid van Moolenbroek 	/*
244*e4e21ee1SDavid van Moolenbroek 	 * We must verify that no entry already exists with the given name.  In
245*e4e21ee1SDavid van Moolenbroek 	 * addition, if a nonnegative identifier is given, we should use that
246*e4e21ee1SDavid van Moolenbroek 	 * identifier and make sure it does not already exist.  Otherwise, we
247*e4e21ee1SDavid van Moolenbroek 	 * must find a free identifier.  Finally, we sort the dynamic nodes in
248*e4e21ee1SDavid van Moolenbroek 	 * ascending identifier order, so we must find the right place at which
249*e4e21ee1SDavid van Moolenbroek 	 * to insert the new node.
250*e4e21ee1SDavid van Moolenbroek 	 *
251*e4e21ee1SDavid van Moolenbroek 	 * For finding a free identifier, choose an ID that falls (well)
252*e4e21ee1SDavid van Moolenbroek 	 * outside the static range, both to avoid accidental retrieval by an
253*e4e21ee1SDavid van Moolenbroek 	 * application that uses a static ID, and to simplify verifying that
254*e4e21ee1SDavid van Moolenbroek 	 * the ID is indeed free.  The sorting of dynamic nodes by identifier
255*e4e21ee1SDavid van Moolenbroek 	 * ensures that searching for a free identifier is O(n).
256*e4e21ee1SDavid van Moolenbroek 	 *
257*e4e21ee1SDavid van Moolenbroek 	 * At this time, we do not support some NetBSD features.  We do not
258*e4e21ee1SDavid van Moolenbroek 	 * force success if the new node is sufficiently like an existing one.
259*e4e21ee1SDavid van Moolenbroek 	 * Also, we do not use global autoincrement for dynamic identifiers,
260*e4e21ee1SDavid van Moolenbroek 	 * although that could easily be changed.
261*e4e21ee1SDavid van Moolenbroek 	 */
262*e4e21ee1SDavid van Moolenbroek 
263*e4e21ee1SDavid van Moolenbroek 	/* First check the static node array, just for collisions. */
264*e4e21ee1SDavid van Moolenbroek 	for (id = 0; IS_STATIC_ID(parent, id); id++) {
265*e4e21ee1SDavid van Moolenbroek 		node = &parent->node_scptr[id];
266*e4e21ee1SDavid van Moolenbroek 		if (node->node_flags == 0)
267*e4e21ee1SDavid van Moolenbroek 			continue;
268*e4e21ee1SDavid van Moolenbroek 		if (id == given_id || !strcmp(name, node->node_name)) {
269*e4e21ee1SDavid van Moolenbroek 			*idp = id;
270*e4e21ee1SDavid van Moolenbroek 			*nodep = node;
271*e4e21ee1SDavid van Moolenbroek 			return EEXIST;
272*e4e21ee1SDavid van Moolenbroek 		}
273*e4e21ee1SDavid van Moolenbroek 	}
274*e4e21ee1SDavid van Moolenbroek 
275*e4e21ee1SDavid van Moolenbroek 	/*
276*e4e21ee1SDavid van Moolenbroek 	 * Then try to find the place to insert a new dynamic node.  At the
277*e4e21ee1SDavid van Moolenbroek 	 * same time, check for both identifier and name collisions.
278*e4e21ee1SDavid van Moolenbroek 	 */
279*e4e21ee1SDavid van Moolenbroek 	if (given_id >= 0)
280*e4e21ee1SDavid van Moolenbroek 		id = given_id;
281*e4e21ee1SDavid van Moolenbroek 	else
282*e4e21ee1SDavid van Moolenbroek 		id = MAX(CREATE_BASE, parent->node_size);
283*e4e21ee1SDavid van Moolenbroek 
284*e4e21ee1SDavid van Moolenbroek 	for (prevp = &parent->node_dcptr; *prevp != NULL;
285*e4e21ee1SDavid van Moolenbroek 	    prevp = &((*prevp)->dynode_next)) {
286*e4e21ee1SDavid van Moolenbroek 		if ((*prevp)->dynode_id > id)
287*e4e21ee1SDavid van Moolenbroek 			break;
288*e4e21ee1SDavid van Moolenbroek 		if ((*prevp)->dynode_id == id) {
289*e4e21ee1SDavid van Moolenbroek 			if (given_id >= 0) {
290*e4e21ee1SDavid van Moolenbroek 				*idp = id;
291*e4e21ee1SDavid van Moolenbroek 				*nodep = &(*prevp)->dynode_node;
292*e4e21ee1SDavid van Moolenbroek 				return EEXIST;
293*e4e21ee1SDavid van Moolenbroek 			} else
294*e4e21ee1SDavid van Moolenbroek 				id++;
295*e4e21ee1SDavid van Moolenbroek 		}
296*e4e21ee1SDavid van Moolenbroek 		if (!strcmp(name, (*prevp)->dynode_node.node_name)) {
297*e4e21ee1SDavid van Moolenbroek 			*idp = (*prevp)->dynode_id;
298*e4e21ee1SDavid van Moolenbroek 			*nodep = &(*prevp)->dynode_node;
299*e4e21ee1SDavid van Moolenbroek 			return EEXIST;
300*e4e21ee1SDavid van Moolenbroek 		}
301*e4e21ee1SDavid van Moolenbroek 	}
302*e4e21ee1SDavid van Moolenbroek 
303*e4e21ee1SDavid van Moolenbroek 	/* Finally, check the rest of the dynamic nodes for name collisions. */
304*e4e21ee1SDavid van Moolenbroek 	for (dynp = prevp; *dynp != NULL; dynp = &((*dynp)->dynode_next)) {
305*e4e21ee1SDavid van Moolenbroek 		assert((*dynp)->dynode_id > id);
306*e4e21ee1SDavid van Moolenbroek 
307*e4e21ee1SDavid van Moolenbroek 		if (!strcmp(name, (*dynp)->dynode_node.node_name)) {
308*e4e21ee1SDavid van Moolenbroek 			*idp = (*dynp)->dynode_id;
309*e4e21ee1SDavid van Moolenbroek 			*nodep = &(*dynp)->dynode_node;
310*e4e21ee1SDavid van Moolenbroek 			return EEXIST;
311*e4e21ee1SDavid van Moolenbroek 		}
312*e4e21ee1SDavid van Moolenbroek 	}
313*e4e21ee1SDavid van Moolenbroek 
314*e4e21ee1SDavid van Moolenbroek 	*idp = id;
315*e4e21ee1SDavid van Moolenbroek 	*prevpp = prevp;
316*e4e21ee1SDavid van Moolenbroek 	return OK;
317*e4e21ee1SDavid van Moolenbroek }
318*e4e21ee1SDavid van Moolenbroek 
319*e4e21ee1SDavid van Moolenbroek /*
320*e4e21ee1SDavid van Moolenbroek  * Copy in a string from the user process, located at the given remote address,
321*e4e21ee1SDavid van Moolenbroek  * into the given local buffer.  If no buffer is given, just compute the length
322*e4e21ee1SDavid van Moolenbroek  * of the string.  On success, return OK.  If 'sizep' is not NULL, it will be
323*e4e21ee1SDavid van Moolenbroek  * filled with the string size, including the null terminator.  If a non-NULL
324*e4e21ee1SDavid van Moolenbroek  * buffer was given, the string will be copied into the provided buffer (also
325*e4e21ee1SDavid van Moolenbroek  * including null terminator).  Return an error code on failure, which includes
326*e4e21ee1SDavid van Moolenbroek  * the case that no null terminator was found within the range of bytes that
327*e4e21ee1SDavid van Moolenbroek  * would fit in the given buffer.
328*e4e21ee1SDavid van Moolenbroek  */
329*e4e21ee1SDavid van Moolenbroek static int
330*e4e21ee1SDavid van Moolenbroek mib_copyin_str(struct mib_newp * __restrict newp, vir_bytes addr,
331*e4e21ee1SDavid van Moolenbroek 	char * __restrict buf, size_t bufsize, size_t * __restrict sizep)
332*e4e21ee1SDavid van Moolenbroek {
333*e4e21ee1SDavid van Moolenbroek 	char *ptr, *endp;
334*e4e21ee1SDavid van Moolenbroek 	size_t chunk, len;
335*e4e21ee1SDavid van Moolenbroek 	int r;
336*e4e21ee1SDavid van Moolenbroek 
337*e4e21ee1SDavid van Moolenbroek 	assert(newp != NULL);
338*e4e21ee1SDavid van Moolenbroek 	assert(bufsize <= SSIZE_MAX);
339*e4e21ee1SDavid van Moolenbroek 
340*e4e21ee1SDavid van Moolenbroek 	if (addr == 0)
341*e4e21ee1SDavid van Moolenbroek 		return EINVAL;
342*e4e21ee1SDavid van Moolenbroek 
343*e4e21ee1SDavid van Moolenbroek 	/*
344*e4e21ee1SDavid van Moolenbroek 	 * NetBSD has a kernel routine for copying in a string from userland.
345*e4e21ee1SDavid van Moolenbroek 	 * MINIX3 does not, since its system call interface has always relied
346*e4e21ee1SDavid van Moolenbroek 	 * on userland passing in string lengths.  The sysctl(2) API does not
347*e4e21ee1SDavid van Moolenbroek 	 * provide the string length, and thus, we have to do a bit of guess
348*e4e21ee1SDavid van Moolenbroek 	 * work.  If we copy too little at once, performance suffers.  If we
349*e4e21ee1SDavid van Moolenbroek 	 * copy too much at once, we may trigger an unneeded page fault.  Make
350*e4e21ee1SDavid van Moolenbroek 	 * use of page boundaries to strike a balance between those two.  If we
351*e4e21ee1SDavid van Moolenbroek 	 * are requested to just get the string length, use the scratch buffer.
352*e4e21ee1SDavid van Moolenbroek 	 */
353*e4e21ee1SDavid van Moolenbroek 	len = 0;
354*e4e21ee1SDavid van Moolenbroek 
355*e4e21ee1SDavid van Moolenbroek 	while (bufsize > 0) {
356*e4e21ee1SDavid van Moolenbroek 		chunk = PAGE_SIZE - (addr % PAGE_SIZE);
357*e4e21ee1SDavid van Moolenbroek 		if (chunk > bufsize)
358*e4e21ee1SDavid van Moolenbroek 			chunk = bufsize;
359*e4e21ee1SDavid van Moolenbroek 
360*e4e21ee1SDavid van Moolenbroek 		ptr = (buf != NULL) ? &buf[len] : scratch;
361*e4e21ee1SDavid van Moolenbroek 		if ((r = mib_copyin_aux(newp, addr, ptr, chunk)) != OK)
362*e4e21ee1SDavid van Moolenbroek 			return r;
363*e4e21ee1SDavid van Moolenbroek 
364*e4e21ee1SDavid van Moolenbroek 		if ((endp = memchr(ptr, '\0', chunk)) != NULL) {
365*e4e21ee1SDavid van Moolenbroek 			/* A null terminator was found - success. */
366*e4e21ee1SDavid van Moolenbroek 			if (sizep != NULL)
367*e4e21ee1SDavid van Moolenbroek 				*sizep = len + (size_t)(endp - ptr) + 1;
368*e4e21ee1SDavid van Moolenbroek 			return OK;
369*e4e21ee1SDavid van Moolenbroek 		}
370*e4e21ee1SDavid van Moolenbroek 
371*e4e21ee1SDavid van Moolenbroek 		addr += chunk;
372*e4e21ee1SDavid van Moolenbroek 		len += chunk;
373*e4e21ee1SDavid van Moolenbroek 		bufsize -= chunk;
374*e4e21ee1SDavid van Moolenbroek 	}
375*e4e21ee1SDavid van Moolenbroek 
376*e4e21ee1SDavid van Moolenbroek 	/* No null terminator found. */
377*e4e21ee1SDavid van Moolenbroek 	return EINVAL;
378*e4e21ee1SDavid van Moolenbroek }
379*e4e21ee1SDavid van Moolenbroek 
380*e4e21ee1SDavid van Moolenbroek /*
381*e4e21ee1SDavid van Moolenbroek  * Increase the version of the root node, and copy this new version to all
382*e4e21ee1SDavid van Moolenbroek  * nodes on the path to a node, as well as (optionally) that node itself.
383*e4e21ee1SDavid van Moolenbroek  */
384*e4e21ee1SDavid van Moolenbroek static void
385*e4e21ee1SDavid van Moolenbroek mib_upgrade(struct mib_node ** stack, int depth, struct mib_node * node)
386*e4e21ee1SDavid van Moolenbroek {
387*e4e21ee1SDavid van Moolenbroek 	uint32_t ver;
388*e4e21ee1SDavid van Moolenbroek 
389*e4e21ee1SDavid van Moolenbroek 	/*
390*e4e21ee1SDavid van Moolenbroek 	 * The bottom of the stack is always the root node, which determines
391*e4e21ee1SDavid van Moolenbroek 	 * the version of the entire tree.  Do not use version number 0, as a
392*e4e21ee1SDavid van Moolenbroek 	 * zero version number indicates no interest in versions elsewhere.
393*e4e21ee1SDavid van Moolenbroek 	 */
394*e4e21ee1SDavid van Moolenbroek 	assert(depth > 0);
395*e4e21ee1SDavid van Moolenbroek 
396*e4e21ee1SDavid van Moolenbroek 	ver = stack[0]->node_ver + 1;
397*e4e21ee1SDavid van Moolenbroek 	if (ver == 0)
398*e4e21ee1SDavid van Moolenbroek 		ver = 1;
399*e4e21ee1SDavid van Moolenbroek 
400*e4e21ee1SDavid van Moolenbroek 	/* Copy the new version to all the nodes on the path. */
401*e4e21ee1SDavid van Moolenbroek 	while (depth-- > 0)
402*e4e21ee1SDavid van Moolenbroek 		stack[depth]->node_ver = ver;
403*e4e21ee1SDavid van Moolenbroek 
404*e4e21ee1SDavid van Moolenbroek 	if (node != NULL)
405*e4e21ee1SDavid van Moolenbroek 		node->node_ver = stack[0]->node_ver;
406*e4e21ee1SDavid van Moolenbroek }
407*e4e21ee1SDavid van Moolenbroek 
408*e4e21ee1SDavid van Moolenbroek /*
409*e4e21ee1SDavid van Moolenbroek  * Create a node.
410*e4e21ee1SDavid van Moolenbroek  */
411*e4e21ee1SDavid van Moolenbroek static ssize_t
412*e4e21ee1SDavid van Moolenbroek mib_create(struct mib_call * call, struct mib_node * parent,
413*e4e21ee1SDavid van Moolenbroek 	struct mib_oldp * oldp, struct mib_newp * newp,
414*e4e21ee1SDavid van Moolenbroek 	struct mib_node ** stack, int depth)
415*e4e21ee1SDavid van Moolenbroek {
416*e4e21ee1SDavid van Moolenbroek 	struct mib_dynode *dynode, **prevp;
417*e4e21ee1SDavid van Moolenbroek 	struct mib_node *node;
418*e4e21ee1SDavid van Moolenbroek 	struct sysctlnode scn;
419*e4e21ee1SDavid van Moolenbroek 	size_t namelen, size;
420*e4e21ee1SDavid van Moolenbroek 	ssize_t len;
421*e4e21ee1SDavid van Moolenbroek 	bool b;
422*e4e21ee1SDavid van Moolenbroek 	char c;
423*e4e21ee1SDavid van Moolenbroek 	int r, id;
424*e4e21ee1SDavid van Moolenbroek 
425*e4e21ee1SDavid van Moolenbroek 	/* This is a privileged operation. */
426*e4e21ee1SDavid van Moolenbroek 	if (!mib_authed(call))
427*e4e21ee1SDavid van Moolenbroek 		return EPERM;
428*e4e21ee1SDavid van Moolenbroek 
429*e4e21ee1SDavid van Moolenbroek 	/* The parent node must not be marked as read-only. */
430*e4e21ee1SDavid van Moolenbroek 	if (!(parent->node_flags & CTLFLAG_READWRITE))
431*e4e21ee1SDavid van Moolenbroek 		return EPERM;
432*e4e21ee1SDavid van Moolenbroek 
433*e4e21ee1SDavid van Moolenbroek 	/*
434*e4e21ee1SDavid van Moolenbroek 	 * Has the parent reached its child node limit?  This check is entirely
435*e4e21ee1SDavid van Moolenbroek 	 * theoretical as long as we support only 32-bit virtual memory.
436*e4e21ee1SDavid van Moolenbroek 	 */
437*e4e21ee1SDavid van Moolenbroek 	if (parent->node_csize == INT_MAX)
438*e4e21ee1SDavid van Moolenbroek 		return EINVAL;
439*e4e21ee1SDavid van Moolenbroek 	assert(parent->node_clen <= parent->node_csize);
440*e4e21ee1SDavid van Moolenbroek 
441*e4e21ee1SDavid van Moolenbroek 	/* The caller must supply information on the child node to create. */
442*e4e21ee1SDavid van Moolenbroek 	if (newp == NULL)
443*e4e21ee1SDavid van Moolenbroek 		return EINVAL;
444*e4e21ee1SDavid van Moolenbroek 
445*e4e21ee1SDavid van Moolenbroek 	if ((r = mib_copyin(newp, &scn, sizeof(scn))) != OK)
446*e4e21ee1SDavid van Moolenbroek 		return r;
447*e4e21ee1SDavid van Moolenbroek 
448*e4e21ee1SDavid van Moolenbroek 	/*
449*e4e21ee1SDavid van Moolenbroek 	 * We perform as many checks as possible before we start allocating
450*e4e21ee1SDavid van Moolenbroek 	 * memory.  Then again, after allocation, copying in data may still
451*e4e21ee1SDavid van Moolenbroek 	 * fail.  Unlike when setting values, we do not first copy data into a
452*e4e21ee1SDavid van Moolenbroek 	 * temporary buffer here, because we do not need to: if the copy fails,
453*e4e21ee1SDavid van Moolenbroek 	 * the entire create operation fails, so atomicity is not an issue.
454*e4e21ee1SDavid van Moolenbroek 	 */
455*e4e21ee1SDavid van Moolenbroek 	if (SYSCTL_VERS(scn.sysctl_flags) != SYSCTL_VERSION)
456*e4e21ee1SDavid van Moolenbroek 		return EINVAL;
457*e4e21ee1SDavid van Moolenbroek 
458*e4e21ee1SDavid van Moolenbroek 	/*
459*e4e21ee1SDavid van Moolenbroek 	 * If a node version number is given, it must match the version of the
460*e4e21ee1SDavid van Moolenbroek 	 * parent or the root (which is always the bottom of the node stack).
461*e4e21ee1SDavid van Moolenbroek 	 * The given version number is *not* used for the node being created.
462*e4e21ee1SDavid van Moolenbroek 	 */
463*e4e21ee1SDavid van Moolenbroek 	assert(depth > 0);
464*e4e21ee1SDavid van Moolenbroek 
465*e4e21ee1SDavid van Moolenbroek 	if (scn.sysctl_ver != 0 && scn.sysctl_ver != stack[0]->node_ver &&
466*e4e21ee1SDavid van Moolenbroek 	    scn.sysctl_ver != parent->node_ver)
467*e4e21ee1SDavid van Moolenbroek 		return EINVAL;
468*e4e21ee1SDavid van Moolenbroek 
469*e4e21ee1SDavid van Moolenbroek 	/*
470*e4e21ee1SDavid van Moolenbroek 	 * Validate the node flags.  In addition to the NetBSD-allowed flags,
471*e4e21ee1SDavid van Moolenbroek 	 * we also allow UNSIGNED, and leave its interpretation to userland.
472*e4e21ee1SDavid van Moolenbroek 	 */
473*e4e21ee1SDavid van Moolenbroek 	if (SYSCTL_FLAGS(scn.sysctl_flags) &
474*e4e21ee1SDavid van Moolenbroek 	    ~(SYSCTL_USERFLAGS | CTLFLAG_UNSIGNED))
475*e4e21ee1SDavid van Moolenbroek 		return EINVAL;
476*e4e21ee1SDavid van Moolenbroek 
477*e4e21ee1SDavid van Moolenbroek 	if (!(scn.sysctl_flags & CTLFLAG_IMMEDIATE)) {
478*e4e21ee1SDavid van Moolenbroek 		/*
479*e4e21ee1SDavid van Moolenbroek 		 * Without either IMMEDIATE or OWNDATA, data pointers are
480*e4e21ee1SDavid van Moolenbroek 		 * actually kernel addresses--a concept we do not support.
481*e4e21ee1SDavid van Moolenbroek 		 * Otherwise, if IMMEDIATE is not set, we are going to have to
482*e4e21ee1SDavid van Moolenbroek 		 * allocate extra memory for the data, so force OWNDATA to be.
483*e4e21ee1SDavid van Moolenbroek 		 * set.  Node-type nodes have no data, though.
484*e4e21ee1SDavid van Moolenbroek 		 */
485*e4e21ee1SDavid van Moolenbroek 		if (SYSCTL_TYPE(scn.sysctl_flags) != CTLTYPE_NODE) {
486*e4e21ee1SDavid van Moolenbroek 			if (!(scn.sysctl_flags & CTLFLAG_OWNDATA) &&
487*e4e21ee1SDavid van Moolenbroek 			    scn.sysctl_data != NULL)
488*e4e21ee1SDavid van Moolenbroek 				return EINVAL; /* not meaningful on MINIX3 */
489*e4e21ee1SDavid van Moolenbroek 
490*e4e21ee1SDavid van Moolenbroek 			scn.sysctl_flags |= CTLFLAG_OWNDATA;
491*e4e21ee1SDavid van Moolenbroek 		}
492*e4e21ee1SDavid van Moolenbroek 	} else if (scn.sysctl_flags & CTLFLAG_OWNDATA)
493*e4e21ee1SDavid van Moolenbroek 		return EINVAL;
494*e4e21ee1SDavid van Moolenbroek 
495*e4e21ee1SDavid van Moolenbroek 	/* The READWRITE flag consists of multiple bits.  Sanitize. */
496*e4e21ee1SDavid van Moolenbroek 	if (scn.sysctl_flags & CTLFLAG_READWRITE)
497*e4e21ee1SDavid van Moolenbroek 		scn.sysctl_flags |= CTLFLAG_READWRITE;
498*e4e21ee1SDavid van Moolenbroek 
499*e4e21ee1SDavid van Moolenbroek 	/* Validate the node type and size, and do some additional checks. */
500*e4e21ee1SDavid van Moolenbroek 	switch (SYSCTL_TYPE(scn.sysctl_flags)) {
501*e4e21ee1SDavid van Moolenbroek 	case CTLTYPE_BOOL:
502*e4e21ee1SDavid van Moolenbroek 		if (scn.sysctl_size != sizeof(bool))
503*e4e21ee1SDavid van Moolenbroek 			return EINVAL;
504*e4e21ee1SDavid van Moolenbroek 		break;
505*e4e21ee1SDavid van Moolenbroek 	case CTLTYPE_INT:
506*e4e21ee1SDavid van Moolenbroek 		if (scn.sysctl_size != sizeof(int))
507*e4e21ee1SDavid van Moolenbroek 			return EINVAL;
508*e4e21ee1SDavid van Moolenbroek 		break;
509*e4e21ee1SDavid van Moolenbroek 	case CTLTYPE_QUAD:
510*e4e21ee1SDavid van Moolenbroek 		if (scn.sysctl_size != sizeof(u_quad_t))
511*e4e21ee1SDavid van Moolenbroek 			return EINVAL;
512*e4e21ee1SDavid van Moolenbroek 		break;
513*e4e21ee1SDavid van Moolenbroek 	case CTLTYPE_STRING:
514*e4e21ee1SDavid van Moolenbroek 		/*
515*e4e21ee1SDavid van Moolenbroek 		 * For strings, a zero length means that we are supposed to
516*e4e21ee1SDavid van Moolenbroek 		 * allocate a buffer size based on the given string size.
517*e4e21ee1SDavid van Moolenbroek 		 */
518*e4e21ee1SDavid van Moolenbroek 		if (scn.sysctl_size == 0 && scn.sysctl_data != NULL) {
519*e4e21ee1SDavid van Moolenbroek 			if ((r = mib_copyin_str(newp,
520*e4e21ee1SDavid van Moolenbroek 			    (vir_bytes)scn.sysctl_data, NULL, SSIZE_MAX,
521*e4e21ee1SDavid van Moolenbroek 			    &size)) != OK)
522*e4e21ee1SDavid van Moolenbroek 				return r;
523*e4e21ee1SDavid van Moolenbroek 			scn.sysctl_size = size;
524*e4e21ee1SDavid van Moolenbroek 		}
525*e4e21ee1SDavid van Moolenbroek 		/* FALLTHROUGH */
526*e4e21ee1SDavid van Moolenbroek 	case CTLTYPE_STRUCT:
527*e4e21ee1SDavid van Moolenbroek 		/*
528*e4e21ee1SDavid van Moolenbroek 		 * We do not set an upper size on the data size, since it would
529*e4e21ee1SDavid van Moolenbroek 		 * still be possible to create a large number of nodes, and
530*e4e21ee1SDavid van Moolenbroek 		 * this is a privileged operation ayway.
531*e4e21ee1SDavid van Moolenbroek 		 */
532*e4e21ee1SDavid van Moolenbroek 		if (scn.sysctl_size == 0 || scn.sysctl_size > SSIZE_MAX)
533*e4e21ee1SDavid van Moolenbroek 			return EINVAL;
534*e4e21ee1SDavid van Moolenbroek 		if (scn.sysctl_flags & CTLFLAG_IMMEDIATE)
535*e4e21ee1SDavid van Moolenbroek 			return EINVAL;
536*e4e21ee1SDavid van Moolenbroek 		break;
537*e4e21ee1SDavid van Moolenbroek 	case CTLTYPE_NODE:
538*e4e21ee1SDavid van Moolenbroek 		/*
539*e4e21ee1SDavid van Moolenbroek 		 * The zero size is an API requirement, but we also rely on the
540*e4e21ee1SDavid van Moolenbroek 		 * zero value internally, as the node has no static children.
541*e4e21ee1SDavid van Moolenbroek 		 */
542*e4e21ee1SDavid van Moolenbroek 		if (scn.sysctl_size != 0)
543*e4e21ee1SDavid van Moolenbroek 			return EINVAL;
544*e4e21ee1SDavid van Moolenbroek 		if (scn.sysctl_flags & (CTLFLAG_IMMEDIATE | CTLFLAG_OWNDATA))
545*e4e21ee1SDavid van Moolenbroek 			return EINVAL;
546*e4e21ee1SDavid van Moolenbroek 		if (scn.sysctl_csize != 0 || scn.sysctl_clen != 0 ||
547*e4e21ee1SDavid van Moolenbroek 		    scn.sysctl_child != NULL)
548*e4e21ee1SDavid van Moolenbroek 			return EINVAL;
549*e4e21ee1SDavid van Moolenbroek 		break;
550*e4e21ee1SDavid van Moolenbroek 	default:
551*e4e21ee1SDavid van Moolenbroek 		return EINVAL;
552*e4e21ee1SDavid van Moolenbroek 	}
553*e4e21ee1SDavid van Moolenbroek 
554*e4e21ee1SDavid van Moolenbroek 	if (scn.sysctl_func != NULL || scn.sysctl_parent != NULL)
555*e4e21ee1SDavid van Moolenbroek 		return EINVAL;
556*e4e21ee1SDavid van Moolenbroek 
557*e4e21ee1SDavid van Moolenbroek 	/* Names must be nonempty, null terminated, C symbol style strings. */
558*e4e21ee1SDavid van Moolenbroek 	for (namelen = 0; namelen < sizeof(scn.sysctl_name); namelen++) {
559*e4e21ee1SDavid van Moolenbroek 		if ((c = scn.sysctl_name[namelen]) == '\0')
560*e4e21ee1SDavid van Moolenbroek 			break;
561*e4e21ee1SDavid van Moolenbroek 		/* A-Z, a-z, 0-9, _ only, and no digit as first character. */
562*e4e21ee1SDavid van Moolenbroek 		if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
563*e4e21ee1SDavid van Moolenbroek 		    c == '_' || (c >= '0' && c <= '9' && namelen > 0)))
564*e4e21ee1SDavid van Moolenbroek 			return EINVAL;
565*e4e21ee1SDavid van Moolenbroek 	}
566*e4e21ee1SDavid van Moolenbroek 	if (namelen == 0 || namelen == sizeof(scn.sysctl_name))
567*e4e21ee1SDavid van Moolenbroek 		return EINVAL;
568*e4e21ee1SDavid van Moolenbroek 
569*e4e21ee1SDavid van Moolenbroek 	/*
570*e4e21ee1SDavid van Moolenbroek 	 * Find a free identifier, or check for ID collisions if a specific
571*e4e21ee1SDavid van Moolenbroek 	 * identifier was given.  At the same time, scan for name collisions,
572*e4e21ee1SDavid van Moolenbroek 	 * and find the location at which to insert the new node in the list.
573*e4e21ee1SDavid van Moolenbroek 	 */
574*e4e21ee1SDavid van Moolenbroek 	r = mib_scan(parent, scn.sysctl_num, scn.sysctl_name, &id, &prevp,
575*e4e21ee1SDavid van Moolenbroek 	    &node);
576*e4e21ee1SDavid van Moolenbroek 
577*e4e21ee1SDavid van Moolenbroek 	if (r != OK) {
578*e4e21ee1SDavid van Moolenbroek 		/*
579*e4e21ee1SDavid van Moolenbroek 		 * On collisions, if requested, copy out the existing node.
580*e4e21ee1SDavid van Moolenbroek 		 * This does not quite fit the general interaction model, as
581*e4e21ee1SDavid van Moolenbroek 		 * the service must now return a nonzero old length from a call
582*e4e21ee1SDavid van Moolenbroek 		 * that actually failed (in contrast to ENOMEM failures).
583*e4e21ee1SDavid van Moolenbroek 		 */
584*e4e21ee1SDavid van Moolenbroek 		if (r == EEXIST && oldp != NULL) {
585*e4e21ee1SDavid van Moolenbroek 			len = mib_copyout_node(call, oldp, 0, id, node);
586*e4e21ee1SDavid van Moolenbroek 
587*e4e21ee1SDavid van Moolenbroek 			if (len > 0)
588*e4e21ee1SDavid van Moolenbroek 				mib_setoldlen(call, len);
589*e4e21ee1SDavid van Moolenbroek 		}
590*e4e21ee1SDavid van Moolenbroek 
591*e4e21ee1SDavid van Moolenbroek 		return r;
592*e4e21ee1SDavid van Moolenbroek 	}
593*e4e21ee1SDavid van Moolenbroek 
594*e4e21ee1SDavid van Moolenbroek 	/*
595*e4e21ee1SDavid van Moolenbroek 	 * All checks so far have passed.  "id" now contains the new node
596*e4e21ee1SDavid van Moolenbroek 	 * identifier, and "prevp" points to the pointer at which to insert the
597*e4e21ee1SDavid van Moolenbroek 	 * new node in its parent's linked list of dynamic nodes.
598*e4e21ee1SDavid van Moolenbroek 	 *
599*e4e21ee1SDavid van Moolenbroek 	 * We can now attempt to create and initialize a new dynamic node.
600*e4e21ee1SDavid van Moolenbroek 	 * Allocating nodes this way may cause heavy memory fragmentation over
601*e4e21ee1SDavid van Moolenbroek 	 * time, but we do not expect the tree to see heavy modification at run
602*e4e21ee1SDavid van Moolenbroek 	 * time, and the superuser has easier ways to get the MIB service in
603*e4e21ee1SDavid van Moolenbroek 	 * trouble.  We note that even in low-memory conditions, the MIB
604*e4e21ee1SDavid van Moolenbroek 	 * service is always able to provide basic functionality.
605*e4e21ee1SDavid van Moolenbroek 	 */
606*e4e21ee1SDavid van Moolenbroek 	size = sizeof(*dynode) + namelen;
607*e4e21ee1SDavid van Moolenbroek 	if (!(scn.sysctl_flags & CTLFLAG_IMMEDIATE))
608*e4e21ee1SDavid van Moolenbroek 		size += scn.sysctl_size;
609*e4e21ee1SDavid van Moolenbroek 
610*e4e21ee1SDavid van Moolenbroek 	if ((dynode = malloc(size)) == NULL)
611*e4e21ee1SDavid van Moolenbroek 		return EINVAL; /* do not return ENOMEM */
612*e4e21ee1SDavid van Moolenbroek 	objects++;
613*e4e21ee1SDavid van Moolenbroek 
614*e4e21ee1SDavid van Moolenbroek 	/* From here on, we have to free "dynode" before returning an error. */
615*e4e21ee1SDavid van Moolenbroek 	r = OK;
616*e4e21ee1SDavid van Moolenbroek 
617*e4e21ee1SDavid van Moolenbroek 	memset(dynode, 0, sizeof(*dynode)); /* no need to zero all of "size" */
618*e4e21ee1SDavid van Moolenbroek 	dynode->dynode_id = id;
619*e4e21ee1SDavid van Moolenbroek 	strlcpy(dynode->dynode_name, scn.sysctl_name, namelen + 1);
620*e4e21ee1SDavid van Moolenbroek 
621*e4e21ee1SDavid van Moolenbroek 	node = &dynode->dynode_node;
622*e4e21ee1SDavid van Moolenbroek 	node->node_flags = scn.sysctl_flags & ~SYSCTL_VERS_MASK;
623*e4e21ee1SDavid van Moolenbroek 	if (SYSCTL_TYPE(scn.sysctl_flags) == CTLTYPE_NODE)
624*e4e21ee1SDavid van Moolenbroek 		node->node_flags |= CTLFLAG_PARENT;
625*e4e21ee1SDavid van Moolenbroek 	node->node_size = scn.sysctl_size;
626*e4e21ee1SDavid van Moolenbroek 	node->node_name = dynode->dynode_name;
627*e4e21ee1SDavid van Moolenbroek 
628*e4e21ee1SDavid van Moolenbroek 	/* Initialize the node value. */
629*e4e21ee1SDavid van Moolenbroek 	if (scn.sysctl_flags & CTLFLAG_IMMEDIATE) {
630*e4e21ee1SDavid van Moolenbroek 		switch (SYSCTL_TYPE(scn.sysctl_flags)) {
631*e4e21ee1SDavid van Moolenbroek 		case CTLTYPE_BOOL:
632*e4e21ee1SDavid van Moolenbroek 			/* Sanitize booleans.  See the C99 _Bool comment. */
633*e4e21ee1SDavid van Moolenbroek 			memcpy(&c, &scn.sysctl_bdata, sizeof(c));
634*e4e21ee1SDavid van Moolenbroek 			node->node_bool = (bool)c;
635*e4e21ee1SDavid van Moolenbroek 			break;
636*e4e21ee1SDavid van Moolenbroek 		case CTLTYPE_INT:
637*e4e21ee1SDavid van Moolenbroek 			node->node_int = scn.sysctl_idata;
638*e4e21ee1SDavid van Moolenbroek 			break;
639*e4e21ee1SDavid van Moolenbroek 		case CTLTYPE_QUAD:
640*e4e21ee1SDavid van Moolenbroek 			node->node_quad = scn.sysctl_qdata;
641*e4e21ee1SDavid van Moolenbroek 			break;
642*e4e21ee1SDavid van Moolenbroek 		default:
643*e4e21ee1SDavid van Moolenbroek 			assert(0);
644*e4e21ee1SDavid van Moolenbroek 		}
645*e4e21ee1SDavid van Moolenbroek 	} else if (SYSCTL_TYPE(scn.sysctl_flags) != CTLTYPE_NODE) {
646*e4e21ee1SDavid van Moolenbroek 		node->node_data = dynode->dynode_name + namelen + 1;
647*e4e21ee1SDavid van Moolenbroek 
648*e4e21ee1SDavid van Moolenbroek 		/* Did the user supply initial data?  If not, use zeroes. */
649*e4e21ee1SDavid van Moolenbroek 		if (scn.sysctl_data != NULL) {
650*e4e21ee1SDavid van Moolenbroek 			/*
651*e4e21ee1SDavid van Moolenbroek 			 * For strings, do not copy in more than needed.  This
652*e4e21ee1SDavid van Moolenbroek 			 * is just a nice feature which allows initialization
653*e4e21ee1SDavid van Moolenbroek 			 * of large string buffers with short strings.
654*e4e21ee1SDavid van Moolenbroek 			 */
655*e4e21ee1SDavid van Moolenbroek 			if (SYSCTL_TYPE(scn.sysctl_flags) == CTLTYPE_STRING)
656*e4e21ee1SDavid van Moolenbroek 				r = mib_copyin_str(newp,
657*e4e21ee1SDavid van Moolenbroek 				    (vir_bytes)scn.sysctl_data,
658*e4e21ee1SDavid van Moolenbroek 				    node->node_data, scn.sysctl_size, NULL);
659*e4e21ee1SDavid van Moolenbroek 			else
660*e4e21ee1SDavid van Moolenbroek 				r = mib_copyin_aux(newp,
661*e4e21ee1SDavid van Moolenbroek 				    (vir_bytes)scn.sysctl_data,
662*e4e21ee1SDavid van Moolenbroek 				    node->node_data, scn.sysctl_size);
663*e4e21ee1SDavid van Moolenbroek 		} else
664*e4e21ee1SDavid van Moolenbroek 			memset(node->node_data, 0, scn.sysctl_size);
665*e4e21ee1SDavid van Moolenbroek 
666*e4e21ee1SDavid van Moolenbroek 		/*
667*e4e21ee1SDavid van Moolenbroek 		 * Sanitize booleans.  See the C99 _Bool comment elsewhere.
668*e4e21ee1SDavid van Moolenbroek 		 * In this case it is not as big of a deal, as we will not be
669*e4e21ee1SDavid van Moolenbroek 		 * accessing the boolean value directly ourselves.
670*e4e21ee1SDavid van Moolenbroek 		 */
671*e4e21ee1SDavid van Moolenbroek 		if (r == OK && SYSCTL_TYPE(scn.sysctl_flags) == CTLTYPE_BOOL) {
672*e4e21ee1SDavid van Moolenbroek 			b = (bool)*(char *)node->node_data;
673*e4e21ee1SDavid van Moolenbroek 			memcpy(node->node_data, &b, sizeof(b));
674*e4e21ee1SDavid van Moolenbroek 		}
675*e4e21ee1SDavid van Moolenbroek 	}
676*e4e21ee1SDavid van Moolenbroek 
677*e4e21ee1SDavid van Moolenbroek 	/*
678*e4e21ee1SDavid van Moolenbroek 	 * Even though it would be entirely possible to set a description right
679*e4e21ee1SDavid van Moolenbroek 	 * away as well, this does not seem to be supported on NetBSD at all.
680*e4e21ee1SDavid van Moolenbroek 	 */
681*e4e21ee1SDavid van Moolenbroek 
682*e4e21ee1SDavid van Moolenbroek 	/* Deal with earlier failures now. */
683*e4e21ee1SDavid van Moolenbroek 	if (r != OK) {
684*e4e21ee1SDavid van Moolenbroek 		free(dynode);
685*e4e21ee1SDavid van Moolenbroek 		objects--;
686*e4e21ee1SDavid van Moolenbroek 
687*e4e21ee1SDavid van Moolenbroek 		return r;
688*e4e21ee1SDavid van Moolenbroek 	}
689*e4e21ee1SDavid van Moolenbroek 
690*e4e21ee1SDavid van Moolenbroek 	/* At this point, actual creation can no longer fail. */
691*e4e21ee1SDavid van Moolenbroek 
692*e4e21ee1SDavid van Moolenbroek 	/* Link the dynamic node into the list, in the right place. */
693*e4e21ee1SDavid van Moolenbroek 	assert(prevp != NULL);
694*e4e21ee1SDavid van Moolenbroek 	dynode->dynode_next = *prevp;
695*e4e21ee1SDavid van Moolenbroek 	*prevp = dynode;
696*e4e21ee1SDavid van Moolenbroek 
697*e4e21ee1SDavid van Moolenbroek 	/* The parent node now has one more child. */
698*e4e21ee1SDavid van Moolenbroek 	parent->node_csize++;
699*e4e21ee1SDavid van Moolenbroek 	parent->node_clen++;
700*e4e21ee1SDavid van Moolenbroek 
701*e4e21ee1SDavid van Moolenbroek 	nodes++;
702*e4e21ee1SDavid van Moolenbroek 
703*e4e21ee1SDavid van Moolenbroek 	/*
704*e4e21ee1SDavid van Moolenbroek 	 * Bump the version of all nodes on the path to the new node, including
705*e4e21ee1SDavid van Moolenbroek 	 * the node itself.
706*e4e21ee1SDavid van Moolenbroek 	 */
707*e4e21ee1SDavid van Moolenbroek 	mib_upgrade(stack, depth, node);
708*e4e21ee1SDavid van Moolenbroek 
709*e4e21ee1SDavid van Moolenbroek 	/*
710*e4e21ee1SDavid van Moolenbroek 	 * Copy out the newly created node as resulting ("old") data.  Do not
711*e4e21ee1SDavid van Moolenbroek 	 * undo the creation if this fails, though.
712*e4e21ee1SDavid van Moolenbroek 	 */
713*e4e21ee1SDavid van Moolenbroek 	return mib_copyout_node(call, oldp, 0, id, node);
714*e4e21ee1SDavid van Moolenbroek }
715*e4e21ee1SDavid van Moolenbroek 
716*e4e21ee1SDavid van Moolenbroek /*
717*e4e21ee1SDavid van Moolenbroek  * Destroy a node.
718*e4e21ee1SDavid van Moolenbroek  */
719*e4e21ee1SDavid van Moolenbroek static ssize_t
720*e4e21ee1SDavid van Moolenbroek mib_destroy(struct mib_call * call, struct mib_node * parent,
721*e4e21ee1SDavid van Moolenbroek 	struct mib_oldp * oldp, struct mib_newp * newp,
722*e4e21ee1SDavid van Moolenbroek 	struct mib_node ** stack, int depth)
723*e4e21ee1SDavid van Moolenbroek {
724*e4e21ee1SDavid van Moolenbroek 	struct mib_dynode *dynode, **prevp;
725*e4e21ee1SDavid van Moolenbroek 	struct mib_node *node;
726*e4e21ee1SDavid van Moolenbroek 	struct sysctlnode scn;
727*e4e21ee1SDavid van Moolenbroek 	ssize_t r;
728*e4e21ee1SDavid van Moolenbroek 
729*e4e21ee1SDavid van Moolenbroek 	/* This is a privileged operation. */
730*e4e21ee1SDavid van Moolenbroek 	if (!mib_authed(call))
731*e4e21ee1SDavid van Moolenbroek 		return EPERM;
732*e4e21ee1SDavid van Moolenbroek 
733*e4e21ee1SDavid van Moolenbroek 	/* The parent node must not be marked as read-only. */
734*e4e21ee1SDavid van Moolenbroek 	if (!(parent->node_flags & CTLFLAG_READWRITE))
735*e4e21ee1SDavid van Moolenbroek 		return EPERM;
736*e4e21ee1SDavid van Moolenbroek 
737*e4e21ee1SDavid van Moolenbroek 	/* The caller must specify which child node to destroy. */
738*e4e21ee1SDavid van Moolenbroek 	if (newp == NULL)
739*e4e21ee1SDavid van Moolenbroek 		return EINVAL;
740*e4e21ee1SDavid van Moolenbroek 
741*e4e21ee1SDavid van Moolenbroek 	if ((r = mib_copyin(newp, &scn, sizeof(scn))) != OK)
742*e4e21ee1SDavid van Moolenbroek 		return r;
743*e4e21ee1SDavid van Moolenbroek 
744*e4e21ee1SDavid van Moolenbroek 	if (SYSCTL_VERS(scn.sysctl_flags) != SYSCTL_VERSION)
745*e4e21ee1SDavid van Moolenbroek 		return EINVAL;
746*e4e21ee1SDavid van Moolenbroek 
747*e4e21ee1SDavid van Moolenbroek 	/* Locate the child node. */
748*e4e21ee1SDavid van Moolenbroek 	if ((node = mib_find(parent, scn.sysctl_num, &prevp)) == NULL)
749*e4e21ee1SDavid van Moolenbroek 		return ENOENT;
750*e4e21ee1SDavid van Moolenbroek 
751*e4e21ee1SDavid van Moolenbroek 	/* The node must not be marked as permanent. */
752*e4e21ee1SDavid van Moolenbroek 	if (node->node_flags & CTLFLAG_PERMANENT)
753*e4e21ee1SDavid van Moolenbroek 		return EPERM;
754*e4e21ee1SDavid van Moolenbroek 
755*e4e21ee1SDavid van Moolenbroek 	/* For node-type nodes, extra rules apply. */
756*e4e21ee1SDavid van Moolenbroek 	if (SYSCTL_TYPE(node->node_flags) == CTLTYPE_NODE) {
757*e4e21ee1SDavid van Moolenbroek 		/* The node must not have an associated function. */
758*e4e21ee1SDavid van Moolenbroek 		if (!(node->node_flags & CTLFLAG_PARENT))
759*e4e21ee1SDavid van Moolenbroek 			return EPERM;
760*e4e21ee1SDavid van Moolenbroek 
761*e4e21ee1SDavid van Moolenbroek 		/* The target node must itself not have child nodes. */
762*e4e21ee1SDavid van Moolenbroek 		if (node->node_clen != 0)
763*e4e21ee1SDavid van Moolenbroek 			return ENOTEMPTY;
764*e4e21ee1SDavid van Moolenbroek 	}
765*e4e21ee1SDavid van Moolenbroek 
766*e4e21ee1SDavid van Moolenbroek 	/* If the user supplied a version, it must match the node version. */
767*e4e21ee1SDavid van Moolenbroek 	if (scn.sysctl_ver != 0 && scn.sysctl_ver != node->node_ver)
768*e4e21ee1SDavid van Moolenbroek 		return EINVAL;	/* NetBSD inconsistently throws ENOENT here */
769*e4e21ee1SDavid van Moolenbroek 
770*e4e21ee1SDavid van Moolenbroek 	/* If the user supplied a name, it must match the node name. */
771*e4e21ee1SDavid van Moolenbroek 	if (scn.sysctl_name[0] != '\0') {
772*e4e21ee1SDavid van Moolenbroek 		if (memchr(scn.sysctl_name, '\0',
773*e4e21ee1SDavid van Moolenbroek 		    sizeof(scn.sysctl_name)) == NULL)
774*e4e21ee1SDavid van Moolenbroek 			return EINVAL;
775*e4e21ee1SDavid van Moolenbroek 		if (strcmp(scn.sysctl_name, node->node_name))
776*e4e21ee1SDavid van Moolenbroek 			return EINVAL;	/* also ENOENT on NetBSD */
777*e4e21ee1SDavid van Moolenbroek 	}
778*e4e21ee1SDavid van Moolenbroek 
779*e4e21ee1SDavid van Moolenbroek 	/*
780*e4e21ee1SDavid van Moolenbroek 	 * Copy out the old node if requested, otherwise return the length
781*e4e21ee1SDavid van Moolenbroek 	 * anyway.  The node will be destroyed even if this call fails,
782*e4e21ee1SDavid van Moolenbroek 	 * because that is how NetBSD behaves.
783*e4e21ee1SDavid van Moolenbroek 	 */
784*e4e21ee1SDavid van Moolenbroek 	r = mib_copyout_node(call, oldp, 0, scn.sysctl_num, node);
785*e4e21ee1SDavid van Moolenbroek 
786*e4e21ee1SDavid van Moolenbroek 	/* If the description was allocated, free it. */
787*e4e21ee1SDavid van Moolenbroek 	if (node->node_flags & CTLFLAG_OWNDESC) {
788*e4e21ee1SDavid van Moolenbroek 		free(__UNCONST(node->node_desc));
789*e4e21ee1SDavid van Moolenbroek 		objects--;
790*e4e21ee1SDavid van Moolenbroek 	}
791*e4e21ee1SDavid van Moolenbroek 
792*e4e21ee1SDavid van Moolenbroek 	/*
793*e4e21ee1SDavid van Moolenbroek 	 * Static nodes only use static memory, and dynamic nodes have the data
794*e4e21ee1SDavid van Moolenbroek 	 * area embedded in the dynode object.  In neither case is data memory
795*e4e21ee1SDavid van Moolenbroek 	 * allocated separately, and thus, it need never be freed separately.
796*e4e21ee1SDavid van Moolenbroek 	 * Therefore we *must not* check CTLFLAG_OWNDATA here.
797*e4e21ee1SDavid van Moolenbroek 	 */
798*e4e21ee1SDavid van Moolenbroek 
799*e4e21ee1SDavid van Moolenbroek 	assert(parent->node_csize > 0);
800*e4e21ee1SDavid van Moolenbroek 	assert(parent->node_clen > 0);
801*e4e21ee1SDavid van Moolenbroek 
802*e4e21ee1SDavid van Moolenbroek 	/*
803*e4e21ee1SDavid van Moolenbroek 	 * Dynamic nodes must be freed.  Freeing the dynode object also frees
804*e4e21ee1SDavid van Moolenbroek 	 * the node name and any associated data.  Static nodes are zeroed out,
805*e4e21ee1SDavid van Moolenbroek 	 * and the static memory they referenced will become inaccessible.
806*e4e21ee1SDavid van Moolenbroek 	 */
807*e4e21ee1SDavid van Moolenbroek 	if (prevp != NULL) {
808*e4e21ee1SDavid van Moolenbroek 		dynode = *prevp;
809*e4e21ee1SDavid van Moolenbroek 		*prevp = dynode->dynode_next;
810*e4e21ee1SDavid van Moolenbroek 
811*e4e21ee1SDavid van Moolenbroek 		free(dynode);
812*e4e21ee1SDavid van Moolenbroek 		objects--;
813*e4e21ee1SDavid van Moolenbroek 
814*e4e21ee1SDavid van Moolenbroek 		parent->node_csize--;
815*e4e21ee1SDavid van Moolenbroek 	} else
816*e4e21ee1SDavid van Moolenbroek 		memset(node, 0, sizeof(*node));
817*e4e21ee1SDavid van Moolenbroek 
818*e4e21ee1SDavid van Moolenbroek 	parent->node_clen--;
819*e4e21ee1SDavid van Moolenbroek 
820*e4e21ee1SDavid van Moolenbroek 	nodes--;
821*e4e21ee1SDavid van Moolenbroek 
822*e4e21ee1SDavid van Moolenbroek 	/* Bump the version of all nodes on the path to the destroyed node. */
823*e4e21ee1SDavid van Moolenbroek 	mib_upgrade(stack, depth, NULL);
824*e4e21ee1SDavid van Moolenbroek 
825*e4e21ee1SDavid van Moolenbroek 	return r;
826*e4e21ee1SDavid van Moolenbroek }
827*e4e21ee1SDavid van Moolenbroek 
828*e4e21ee1SDavid van Moolenbroek /*
829*e4e21ee1SDavid van Moolenbroek  * Copy out a node description to userland, using the exchange format for node
830*e4e21ee1SDavid van Moolenbroek  * descriptions (namely, a sysctldesc structure).  Return the size of the
831*e4e21ee1SDavid van Moolenbroek  * object that is (or, if the description falls outside the requested data
832*e4e21ee1SDavid van Moolenbroek  * range, would be) copied out on success, or a negative error code on failure.
833*e4e21ee1SDavid van Moolenbroek  * The function may return 0 to indicate that nothing was copied out after all.
834*e4e21ee1SDavid van Moolenbroek  */
835*e4e21ee1SDavid van Moolenbroek static ssize_t
836*e4e21ee1SDavid van Moolenbroek mib_copyout_desc(struct mib_call * call, struct mib_oldp * oldp, size_t off,
837*e4e21ee1SDavid van Moolenbroek 	int id, const struct mib_node * node)
838*e4e21ee1SDavid van Moolenbroek {
839*e4e21ee1SDavid van Moolenbroek 	struct sysctldesc *scd;
840*e4e21ee1SDavid van Moolenbroek 	size_t size;
841*e4e21ee1SDavid van Moolenbroek 	int r;
842*e4e21ee1SDavid van Moolenbroek 
843*e4e21ee1SDavid van Moolenbroek 	/* Descriptions of private nodes are considered private too. */
844*e4e21ee1SDavid van Moolenbroek 	if ((node->node_flags & CTLFLAG_PRIVATE) && !mib_authed(call))
845*e4e21ee1SDavid van Moolenbroek 		return 0;
846*e4e21ee1SDavid van Moolenbroek 
847*e4e21ee1SDavid van Moolenbroek 	/* The description length includes the null terminator. */
848*e4e21ee1SDavid van Moolenbroek 	if (node->node_desc != NULL)
849*e4e21ee1SDavid van Moolenbroek 		size = strlen(node->node_desc) + 1;
850*e4e21ee1SDavid van Moolenbroek 	else
851*e4e21ee1SDavid van Moolenbroek 		size = 1;
852*e4e21ee1SDavid van Moolenbroek 
853*e4e21ee1SDavid van Moolenbroek 	assert(sizeof(*scd) + size <= sizeof(scratch));
854*e4e21ee1SDavid van Moolenbroek 
855*e4e21ee1SDavid van Moolenbroek 	scd = (struct sysctldesc *)scratch;
856*e4e21ee1SDavid van Moolenbroek 	memset(scd, 0, sizeof(*scd));
857*e4e21ee1SDavid van Moolenbroek 	scd->descr_num = id;
858*e4e21ee1SDavid van Moolenbroek 	scd->descr_ver = node->node_ver;
859*e4e21ee1SDavid van Moolenbroek 	scd->descr_len = size;
860*e4e21ee1SDavid van Moolenbroek 	if (node->node_desc != NULL)
861*e4e21ee1SDavid van Moolenbroek 		strlcpy(scd->descr_str, node->node_desc,
862*e4e21ee1SDavid van Moolenbroek 		    sizeof(scratch) - sizeof(*scd));
863*e4e21ee1SDavid van Moolenbroek 	else
864*e4e21ee1SDavid van Moolenbroek 		scd->descr_str[0] = '\0';
865*e4e21ee1SDavid van Moolenbroek 
866*e4e21ee1SDavid van Moolenbroek 	size += offsetof(struct sysctldesc, descr_str);
867*e4e21ee1SDavid van Moolenbroek 
868*e4e21ee1SDavid van Moolenbroek 	if ((r = mib_copyout(oldp, off, scratch, size)) < 0)
869*e4e21ee1SDavid van Moolenbroek 		return r;
870*e4e21ee1SDavid van Moolenbroek 
871*e4e21ee1SDavid van Moolenbroek 	/*
872*e4e21ee1SDavid van Moolenbroek 	 * By aligning just the size, we may leave garbage between the entries
873*e4e21ee1SDavid van Moolenbroek 	 * copied out, which is fine because it is userland's own data.
874*e4e21ee1SDavid van Moolenbroek 	 */
875*e4e21ee1SDavid van Moolenbroek 	return roundup2(size, sizeof(int32_t));
876*e4e21ee1SDavid van Moolenbroek }
877*e4e21ee1SDavid van Moolenbroek 
878*e4e21ee1SDavid van Moolenbroek /*
879*e4e21ee1SDavid van Moolenbroek  * Retrieve node descriptions in bulk, or retrieve or assign a particular
880*e4e21ee1SDavid van Moolenbroek  * node's description.
881*e4e21ee1SDavid van Moolenbroek  */
882*e4e21ee1SDavid van Moolenbroek static ssize_t
883*e4e21ee1SDavid van Moolenbroek mib_describe(struct mib_call * call, struct mib_node * parent,
884*e4e21ee1SDavid van Moolenbroek 	struct mib_oldp * oldp, struct mib_newp * newp)
885*e4e21ee1SDavid van Moolenbroek {
886*e4e21ee1SDavid van Moolenbroek 	struct sysctlnode scn;
887*e4e21ee1SDavid van Moolenbroek 	struct mib_node *node;
888*e4e21ee1SDavid van Moolenbroek 	struct mib_dynode *dynode;
889*e4e21ee1SDavid van Moolenbroek 	size_t off;
890*e4e21ee1SDavid van Moolenbroek 	int r, id;
891*e4e21ee1SDavid van Moolenbroek 
892*e4e21ee1SDavid van Moolenbroek 	/* If new data are given, they identify a particular target node. */
893*e4e21ee1SDavid van Moolenbroek 	if (newp != NULL) {
894*e4e21ee1SDavid van Moolenbroek 		if ((r = mib_copyin(newp, &scn, sizeof(scn))) != OK)
895*e4e21ee1SDavid van Moolenbroek 			return r;
896*e4e21ee1SDavid van Moolenbroek 
897*e4e21ee1SDavid van Moolenbroek 		if (SYSCTL_VERS(scn.sysctl_flags) != SYSCTL_VERSION)
898*e4e21ee1SDavid van Moolenbroek 			return EINVAL;
899*e4e21ee1SDavid van Moolenbroek 
900*e4e21ee1SDavid van Moolenbroek 		/* Locate the child node. */
901*e4e21ee1SDavid van Moolenbroek 		if ((node = mib_find(parent, scn.sysctl_num, NULL)) == NULL)
902*e4e21ee1SDavid van Moolenbroek 			return ENOENT;
903*e4e21ee1SDavid van Moolenbroek 
904*e4e21ee1SDavid van Moolenbroek 		/* Descriptions of private nodes are considered private too. */
905*e4e21ee1SDavid van Moolenbroek 		if ((node->node_flags & CTLFLAG_PRIVATE) && !mib_authed(call))
906*e4e21ee1SDavid van Moolenbroek 			return EPERM;
907*e4e21ee1SDavid van Moolenbroek 
908*e4e21ee1SDavid van Moolenbroek 		/*
909*e4e21ee1SDavid van Moolenbroek 		 * If a description pointer was given, this is a request to
910*e4e21ee1SDavid van Moolenbroek 		 * set the node's description.
911*e4e21ee1SDavid van Moolenbroek 		 */
912*e4e21ee1SDavid van Moolenbroek 		if (scn.sysctl_desc != NULL) {
913*e4e21ee1SDavid van Moolenbroek 			/* Such a request requires superuser privileges. */
914*e4e21ee1SDavid van Moolenbroek 			if (!mib_authed(call))
915*e4e21ee1SDavid van Moolenbroek 				return EPERM;
916*e4e21ee1SDavid van Moolenbroek 
917*e4e21ee1SDavid van Moolenbroek 			/* The node must not already have a description. */
918*e4e21ee1SDavid van Moolenbroek 			if (node->node_desc != NULL)
919*e4e21ee1SDavid van Moolenbroek 				return EPERM;
920*e4e21ee1SDavid van Moolenbroek 
921*e4e21ee1SDavid van Moolenbroek 			/* The node must not be marked as permanent. */
922*e4e21ee1SDavid van Moolenbroek 			if (node->node_flags & CTLFLAG_PERMANENT)
923*e4e21ee1SDavid van Moolenbroek 				return EPERM;
924*e4e21ee1SDavid van Moolenbroek 
925*e4e21ee1SDavid van Moolenbroek 			/*
926*e4e21ee1SDavid van Moolenbroek 			 * If the user supplied a version, it must match.
927*e4e21ee1SDavid van Moolenbroek 			 * NetBSD performs this check only when setting
928*e4e21ee1SDavid van Moolenbroek 			 * descriptions, and thus, so do we..
929*e4e21ee1SDavid van Moolenbroek 			 */
930*e4e21ee1SDavid van Moolenbroek 			if (scn.sysctl_ver != 0 &&
931*e4e21ee1SDavid van Moolenbroek 			    scn.sysctl_ver != node->node_ver)
932*e4e21ee1SDavid van Moolenbroek 				return EINVAL;
933*e4e21ee1SDavid van Moolenbroek 
934*e4e21ee1SDavid van Moolenbroek 			/*
935*e4e21ee1SDavid van Moolenbroek 			 * Copy in the description to the scratch buffer.
936*e4e21ee1SDavid van Moolenbroek 			 * The length of the description must be reasonable.
937*e4e21ee1SDavid van Moolenbroek 			 */
938*e4e21ee1SDavid van Moolenbroek 			if ((r = mib_copyin_str(newp,
939*e4e21ee1SDavid van Moolenbroek 			    (vir_bytes)scn.sysctl_desc, scratch, MAXDESCLEN,
940*e4e21ee1SDavid van Moolenbroek 			    NULL)) != OK)
941*e4e21ee1SDavid van Moolenbroek 				return r;
942*e4e21ee1SDavid van Moolenbroek 
943*e4e21ee1SDavid van Moolenbroek 			/* Allocate memory and store the description. */
944*e4e21ee1SDavid van Moolenbroek 			if ((node->node_desc = strdup(scratch)) == NULL) {
945*e4e21ee1SDavid van Moolenbroek 				printf("MIB: out of memory!\n");
946*e4e21ee1SDavid van Moolenbroek 
947*e4e21ee1SDavid van Moolenbroek 				return EINVAL; /* do not return ENOMEM */
948*e4e21ee1SDavid van Moolenbroek 			}
949*e4e21ee1SDavid van Moolenbroek 			objects++;
950*e4e21ee1SDavid van Moolenbroek 
951*e4e21ee1SDavid van Moolenbroek 			/* The description must now be freed with the node. */
952*e4e21ee1SDavid van Moolenbroek 			node->node_flags |= CTLFLAG_OWNDESC;
953*e4e21ee1SDavid van Moolenbroek 		}
954*e4e21ee1SDavid van Moolenbroek 
955*e4e21ee1SDavid van Moolenbroek 		/*
956*e4e21ee1SDavid van Moolenbroek 		 * Either way, copy out the requested node's description, which
957*e4e21ee1SDavid van Moolenbroek 		 * should indeed be the new description if one was just set.
958*e4e21ee1SDavid van Moolenbroek 		 * Note that we have already performed the permission check
959*e4e21ee1SDavid van Moolenbroek 		 * that could make this call return zero, so here it will not.
960*e4e21ee1SDavid van Moolenbroek 		 */
961*e4e21ee1SDavid van Moolenbroek 		return mib_copyout_desc(call, oldp, 0, scn.sysctl_num, node);
962*e4e21ee1SDavid van Moolenbroek 	}
963*e4e21ee1SDavid van Moolenbroek 
964*e4e21ee1SDavid van Moolenbroek 	/* See also the considerations laid out in mib_query(). */
965*e4e21ee1SDavid van Moolenbroek 	off = 0;
966*e4e21ee1SDavid van Moolenbroek 
967*e4e21ee1SDavid van Moolenbroek 	/* First describe the static nodes. */
968*e4e21ee1SDavid van Moolenbroek 	for (id = 0; IS_STATIC_ID(parent, id); id++) {
969*e4e21ee1SDavid van Moolenbroek 		node = &parent->node_scptr[id];
970*e4e21ee1SDavid van Moolenbroek 
971*e4e21ee1SDavid van Moolenbroek 		if (node->node_flags == 0)
972*e4e21ee1SDavid van Moolenbroek 			continue;
973*e4e21ee1SDavid van Moolenbroek 
974*e4e21ee1SDavid van Moolenbroek 		if ((r = mib_copyout_desc(call, oldp, off, id, node)) < 0)
975*e4e21ee1SDavid van Moolenbroek 			return r;
976*e4e21ee1SDavid van Moolenbroek 		off += r;
977*e4e21ee1SDavid van Moolenbroek 	}
978*e4e21ee1SDavid van Moolenbroek 
979*e4e21ee1SDavid van Moolenbroek 	/* Then describe the dynamic nodes. */
980*e4e21ee1SDavid van Moolenbroek 	for (dynode = parent->node_dcptr; dynode != NULL;
981*e4e21ee1SDavid van Moolenbroek 	    dynode = dynode->dynode_next) {
982*e4e21ee1SDavid van Moolenbroek 		node = &dynode->dynode_node;
983*e4e21ee1SDavid van Moolenbroek 
984*e4e21ee1SDavid van Moolenbroek 		if ((r = mib_copyout_desc(call, oldp, off, dynode->dynode_id,
985*e4e21ee1SDavid van Moolenbroek 		    node)) < 0)
986*e4e21ee1SDavid van Moolenbroek 			return r;
987*e4e21ee1SDavid van Moolenbroek 		off += r;
988*e4e21ee1SDavid van Moolenbroek 	}
989*e4e21ee1SDavid van Moolenbroek 
990*e4e21ee1SDavid van Moolenbroek 	return off;
991*e4e21ee1SDavid van Moolenbroek }
992*e4e21ee1SDavid van Moolenbroek 
993*e4e21ee1SDavid van Moolenbroek /*
994*e4e21ee1SDavid van Moolenbroek  * Return a pointer to the data associated with the given node, or NULL if the
995*e4e21ee1SDavid van Moolenbroek  * node has no associated data.  Actual calls to this function should never
996*e4e21ee1SDavid van Moolenbroek  * result in NULL - as long as the proper rules are followed elsewhere.
997*e4e21ee1SDavid van Moolenbroek  */
998*e4e21ee1SDavid van Moolenbroek static void *
999*e4e21ee1SDavid van Moolenbroek mib_getptr(struct mib_node * node)
1000*e4e21ee1SDavid van Moolenbroek {
1001*e4e21ee1SDavid van Moolenbroek 
1002*e4e21ee1SDavid van Moolenbroek 	switch (SYSCTL_TYPE(node->node_flags)) {
1003*e4e21ee1SDavid van Moolenbroek 	case CTLTYPE_BOOL:
1004*e4e21ee1SDavid van Moolenbroek 		if (node->node_flags & CTLFLAG_IMMEDIATE)
1005*e4e21ee1SDavid van Moolenbroek 			return &node->node_bool;
1006*e4e21ee1SDavid van Moolenbroek 		break;
1007*e4e21ee1SDavid van Moolenbroek 	case CTLTYPE_INT:
1008*e4e21ee1SDavid van Moolenbroek 		if (node->node_flags & CTLFLAG_IMMEDIATE)
1009*e4e21ee1SDavid van Moolenbroek 			return &node->node_int;
1010*e4e21ee1SDavid van Moolenbroek 		break;
1011*e4e21ee1SDavid van Moolenbroek 	case CTLTYPE_QUAD:
1012*e4e21ee1SDavid van Moolenbroek 		if (node->node_flags & CTLFLAG_IMMEDIATE)
1013*e4e21ee1SDavid van Moolenbroek 			return &node->node_quad;
1014*e4e21ee1SDavid van Moolenbroek 		break;
1015*e4e21ee1SDavid van Moolenbroek 	case CTLTYPE_STRING:
1016*e4e21ee1SDavid van Moolenbroek 	case CTLTYPE_STRUCT:
1017*e4e21ee1SDavid van Moolenbroek 		if (node->node_flags & CTLFLAG_IMMEDIATE)
1018*e4e21ee1SDavid van Moolenbroek 			return NULL;
1019*e4e21ee1SDavid van Moolenbroek 		break;
1020*e4e21ee1SDavid van Moolenbroek 	default:
1021*e4e21ee1SDavid van Moolenbroek 		return NULL;
1022*e4e21ee1SDavid van Moolenbroek 	}
1023*e4e21ee1SDavid van Moolenbroek 
1024*e4e21ee1SDavid van Moolenbroek 	return node->node_data;
1025*e4e21ee1SDavid van Moolenbroek }
1026*e4e21ee1SDavid van Moolenbroek 
1027*e4e21ee1SDavid van Moolenbroek /*
1028*e4e21ee1SDavid van Moolenbroek  * Read current (old) data from a regular data node, if requested.  Return the
1029*e4e21ee1SDavid van Moolenbroek  * old data length.
1030*e4e21ee1SDavid van Moolenbroek  */
1031*e4e21ee1SDavid van Moolenbroek static ssize_t
1032*e4e21ee1SDavid van Moolenbroek mib_read(struct mib_node * node, struct mib_oldp * oldp)
1033*e4e21ee1SDavid van Moolenbroek {
1034*e4e21ee1SDavid van Moolenbroek 	void *ptr;
1035*e4e21ee1SDavid van Moolenbroek 	size_t oldlen;
1036*e4e21ee1SDavid van Moolenbroek 	int r;
1037*e4e21ee1SDavid van Moolenbroek 
1038*e4e21ee1SDavid van Moolenbroek 	if ((ptr = mib_getptr(node)) == NULL)
1039*e4e21ee1SDavid van Moolenbroek 		return EINVAL;
1040*e4e21ee1SDavid van Moolenbroek 
1041*e4e21ee1SDavid van Moolenbroek 	if (SYSCTL_TYPE(node->node_flags) == CTLTYPE_STRING)
1042*e4e21ee1SDavid van Moolenbroek 		oldlen = strlen(node->node_data) + 1;
1043*e4e21ee1SDavid van Moolenbroek 	else
1044*e4e21ee1SDavid van Moolenbroek 		oldlen = node->node_size;
1045*e4e21ee1SDavid van Moolenbroek 
1046*e4e21ee1SDavid van Moolenbroek 	if (oldlen > SSIZE_MAX)
1047*e4e21ee1SDavid van Moolenbroek 		return EINVAL;
1048*e4e21ee1SDavid van Moolenbroek 
1049*e4e21ee1SDavid van Moolenbroek 	/* Copy out the current data, if requested at all. */
1050*e4e21ee1SDavid van Moolenbroek 	if (oldp != NULL && (r = mib_copyout(oldp, 0, ptr, oldlen)) < 0)
1051*e4e21ee1SDavid van Moolenbroek 		return r;
1052*e4e21ee1SDavid van Moolenbroek 
1053*e4e21ee1SDavid van Moolenbroek 	/* Return the current length in any case. */
1054*e4e21ee1SDavid van Moolenbroek 	return (ssize_t)oldlen;
1055*e4e21ee1SDavid van Moolenbroek }
1056*e4e21ee1SDavid van Moolenbroek 
1057*e4e21ee1SDavid van Moolenbroek /*
1058*e4e21ee1SDavid van Moolenbroek  * Write new data into a regular data node, if requested.
1059*e4e21ee1SDavid van Moolenbroek  */
1060*e4e21ee1SDavid van Moolenbroek static int
1061*e4e21ee1SDavid van Moolenbroek mib_write(struct mib_call * call, struct mib_node * node,
1062*e4e21ee1SDavid van Moolenbroek 	struct mib_newp * newp, mib_verify_ptr verify)
1063*e4e21ee1SDavid van Moolenbroek {
1064*e4e21ee1SDavid van Moolenbroek 	bool b[(sizeof(bool) == sizeof(char)) ? 1 : -1]; /* explained below */
1065*e4e21ee1SDavid van Moolenbroek 	char *src, *dst;
1066*e4e21ee1SDavid van Moolenbroek 	size_t newlen;
1067*e4e21ee1SDavid van Moolenbroek 	int r;
1068*e4e21ee1SDavid van Moolenbroek 
1069*e4e21ee1SDavid van Moolenbroek 	if (newp == NULL)
1070*e4e21ee1SDavid van Moolenbroek 		return OK; /* nothing to do */
1071*e4e21ee1SDavid van Moolenbroek 
1072*e4e21ee1SDavid van Moolenbroek 	/*
1073*e4e21ee1SDavid van Moolenbroek 	 * When setting a new value, we cannot risk doing an in-place update:
1074*e4e21ee1SDavid van Moolenbroek 	 * the copy from userland may fail halfway through, in which case an
1075*e4e21ee1SDavid van Moolenbroek 	 * in-place update could leave the node value in a corrupted state.
1076*e4e21ee1SDavid van Moolenbroek 	 * Thus, we must first fetch any new data into a temporary buffer.
1077*e4e21ee1SDavid van Moolenbroek 	 *
1078*e4e21ee1SDavid van Moolenbroek 	 * Given that we use intermediate data storage, we could support value
1079*e4e21ee1SDavid van Moolenbroek 	 * swapping, where the user provides the same buffer for new and old
1080*e4e21ee1SDavid van Moolenbroek 	 * data.  We choose not to: NetBSD does not support it, it would make
1081*e4e21ee1SDavid van Moolenbroek 	 * trace(1)'s job a lot harder, and it would convolute the code here.
1082*e4e21ee1SDavid van Moolenbroek 	 */
1083*e4e21ee1SDavid van Moolenbroek 	newlen = mib_getnewlen(newp);
1084*e4e21ee1SDavid van Moolenbroek 
1085*e4e21ee1SDavid van Moolenbroek 	if ((dst = mib_getptr(node)) == NULL)
1086*e4e21ee1SDavid van Moolenbroek 		return EINVAL;
1087*e4e21ee1SDavid van Moolenbroek 
1088*e4e21ee1SDavid van Moolenbroek 	switch (SYSCTL_TYPE(node->node_flags)) {
1089*e4e21ee1SDavid van Moolenbroek 	case CTLTYPE_STRING:
1090*e4e21ee1SDavid van Moolenbroek 		/*
1091*e4e21ee1SDavid van Moolenbroek 		 * Strings must not exceed their buffer size.  There is a
1092*e4e21ee1SDavid van Moolenbroek 		 * second check further below, because we allow userland to
1093*e4e21ee1SDavid van Moolenbroek 		 * give us an unterminated string.  In that case we terminate
1094*e4e21ee1SDavid van Moolenbroek 		 * it ourselves, but then the null terminator must fit as well.
1095*e4e21ee1SDavid van Moolenbroek 		 */
1096*e4e21ee1SDavid van Moolenbroek 		if (newlen > node->node_size)
1097*e4e21ee1SDavid van Moolenbroek 			return EINVAL;
1098*e4e21ee1SDavid van Moolenbroek 		break;
1099*e4e21ee1SDavid van Moolenbroek 	case CTLTYPE_BOOL:
1100*e4e21ee1SDavid van Moolenbroek 	case CTLTYPE_INT:
1101*e4e21ee1SDavid van Moolenbroek 	case CTLTYPE_QUAD:
1102*e4e21ee1SDavid van Moolenbroek 	case CTLTYPE_STRUCT:
1103*e4e21ee1SDavid van Moolenbroek 		/* Non-string types must have an exact size match. */
1104*e4e21ee1SDavid van Moolenbroek 		if (newlen != node->node_size)
1105*e4e21ee1SDavid van Moolenbroek 			return EINVAL;
1106*e4e21ee1SDavid van Moolenbroek 		break;
1107*e4e21ee1SDavid van Moolenbroek 	default:
1108*e4e21ee1SDavid van Moolenbroek 		return EINVAL;
1109*e4e21ee1SDavid van Moolenbroek 	}
1110*e4e21ee1SDavid van Moolenbroek 
1111*e4e21ee1SDavid van Moolenbroek 	/*
1112*e4e21ee1SDavid van Moolenbroek 	 * If we cannot fit the data in the small stack buffer, then allocate a
1113*e4e21ee1SDavid van Moolenbroek 	 * temporary buffer.  We add one extra byte so that we can add a null
1114*e4e21ee1SDavid van Moolenbroek 	 * terminator at the end of strings in case userland did not supply
1115*e4e21ee1SDavid van Moolenbroek 	 * one.  Either way, we must free the temporary buffer later!
1116*e4e21ee1SDavid van Moolenbroek 	 *
1117*e4e21ee1SDavid van Moolenbroek 	 * The alternative is to ensure that the given memory is accessible
1118*e4e21ee1SDavid van Moolenbroek 	 * before starting the copy, but that would break if we ever add kernel
1119*e4e21ee1SDavid van Moolenbroek 	 * threads or anything that allows asynchronous memory unmapping, etc.
1120*e4e21ee1SDavid van Moolenbroek 	 */
1121*e4e21ee1SDavid van Moolenbroek 	if (newlen + 1 > sizeof(scratch)) {
1122*e4e21ee1SDavid van Moolenbroek 		/*
1123*e4e21ee1SDavid van Moolenbroek 		 * In practice, the temporary buffer is at least an entire
1124*e4e21ee1SDavid van Moolenbroek 		 * memory page, which is reasonable by any standard.  As a
1125*e4e21ee1SDavid van Moolenbroek 		 * result, we can get away with refusing to perform dynamic
1126*e4e21ee1SDavid van Moolenbroek 		 * allocation for unprivileged users.  This limits the impact
1127*e4e21ee1SDavid van Moolenbroek 		 * that unprivileged users can have on our memory space.
1128*e4e21ee1SDavid van Moolenbroek 		 */
1129*e4e21ee1SDavid van Moolenbroek 		if (!mib_authed(call))
1130*e4e21ee1SDavid van Moolenbroek 			return EPERM;
1131*e4e21ee1SDavid van Moolenbroek 
1132*e4e21ee1SDavid van Moolenbroek 		/*
1133*e4e21ee1SDavid van Moolenbroek 		 * Do not return ENOMEM on allocation failure, because ENOMEM
1134*e4e21ee1SDavid van Moolenbroek 		 * implies that a valid old length was returned.
1135*e4e21ee1SDavid van Moolenbroek 		 */
1136*e4e21ee1SDavid van Moolenbroek 		if ((src = malloc(newlen + 1)) == NULL) {
1137*e4e21ee1SDavid van Moolenbroek 			printf("MIB: out of memory!\n");
1138*e4e21ee1SDavid van Moolenbroek 
1139*e4e21ee1SDavid van Moolenbroek 			return EINVAL;
1140*e4e21ee1SDavid van Moolenbroek 		}
1141*e4e21ee1SDavid van Moolenbroek 		objects++;
1142*e4e21ee1SDavid van Moolenbroek 	} else
1143*e4e21ee1SDavid van Moolenbroek 		src = scratch;
1144*e4e21ee1SDavid van Moolenbroek 
1145*e4e21ee1SDavid van Moolenbroek 	/* Copy in the data.  Note that newlen may be zero. */
1146*e4e21ee1SDavid van Moolenbroek 	r = mib_copyin(newp, src, newlen);
1147*e4e21ee1SDavid van Moolenbroek 
1148*e4e21ee1SDavid van Moolenbroek 	if (r == OK && verify != NULL && !verify(call, node, src, newlen))
1149*e4e21ee1SDavid van Moolenbroek 		r = EINVAL;
1150*e4e21ee1SDavid van Moolenbroek 
1151*e4e21ee1SDavid van Moolenbroek 	if (r == OK) {
1152*e4e21ee1SDavid van Moolenbroek 		/* Check and, if acceptable, store the new value. */
1153*e4e21ee1SDavid van Moolenbroek 		switch (SYSCTL_TYPE(node->node_flags)) {
1154*e4e21ee1SDavid van Moolenbroek 		case CTLTYPE_BOOL:
1155*e4e21ee1SDavid van Moolenbroek 			/*
1156*e4e21ee1SDavid van Moolenbroek 			 * Due to the nature of the C99 _Bool type, we can not
1157*e4e21ee1SDavid van Moolenbroek 			 * test directly whether the given boolean value is a
1158*e4e21ee1SDavid van Moolenbroek 			 * value that is not "true" and not "false".  In the
1159*e4e21ee1SDavid van Moolenbroek 			 * worst case, another value could invoke undefined
1160*e4e21ee1SDavid van Moolenbroek 			 * behavior.  We try our best to sanitize the value
1161*e4e21ee1SDavid van Moolenbroek 			 * without looking at it directly, which unfortunately
1162*e4e21ee1SDavid van Moolenbroek 			 * requires us to test for the size of the bool type.
1163*e4e21ee1SDavid van Moolenbroek 			 * We do that at compile time, hence the 'b' "array".
1164*e4e21ee1SDavid van Moolenbroek 			 * Any size other than one byte is an ABI violation.
1165*e4e21ee1SDavid van Moolenbroek 			 */
1166*e4e21ee1SDavid van Moolenbroek 			b[0] = (bool)src[0];
1167*e4e21ee1SDavid van Moolenbroek 			memcpy(dst, &b[0], sizeof(b[0]));
1168*e4e21ee1SDavid van Moolenbroek 			break;
1169*e4e21ee1SDavid van Moolenbroek 		case CTLTYPE_INT:
1170*e4e21ee1SDavid van Moolenbroek 		case CTLTYPE_QUAD:
1171*e4e21ee1SDavid van Moolenbroek 		case CTLTYPE_STRUCT:
1172*e4e21ee1SDavid van Moolenbroek 			memcpy(dst, src, node->node_size);
1173*e4e21ee1SDavid van Moolenbroek 			break;
1174*e4e21ee1SDavid van Moolenbroek 		case CTLTYPE_STRING:
1175*e4e21ee1SDavid van Moolenbroek 			if (newlen == node->node_size &&
1176*e4e21ee1SDavid van Moolenbroek 			    src[newlen - 1] != '\0') {
1177*e4e21ee1SDavid van Moolenbroek 				/* Our null terminator does not fit! */
1178*e4e21ee1SDavid van Moolenbroek 				r = EINVAL;
1179*e4e21ee1SDavid van Moolenbroek 				break;
1180*e4e21ee1SDavid van Moolenbroek 			}
1181*e4e21ee1SDavid van Moolenbroek 			/*
1182*e4e21ee1SDavid van Moolenbroek 			 * We do not mind null characters in the middle.  In
1183*e4e21ee1SDavid van Moolenbroek 			 * general, the buffer may contain garbage after the
1184*e4e21ee1SDavid van Moolenbroek 			 * first null terminator, but such garbage will never
1185*e4e21ee1SDavid van Moolenbroek 			 * end up being copied out.
1186*e4e21ee1SDavid van Moolenbroek 			 */
1187*e4e21ee1SDavid van Moolenbroek 			src[newlen] = '\0';
1188*e4e21ee1SDavid van Moolenbroek 			strlcpy(dst, src, node->node_size);
1189*e4e21ee1SDavid van Moolenbroek 			break;
1190*e4e21ee1SDavid van Moolenbroek 		default:
1191*e4e21ee1SDavid van Moolenbroek 			r = EINVAL;
1192*e4e21ee1SDavid van Moolenbroek 		}
1193*e4e21ee1SDavid van Moolenbroek 	}
1194*e4e21ee1SDavid van Moolenbroek 
1195*e4e21ee1SDavid van Moolenbroek 	if (src != scratch) {
1196*e4e21ee1SDavid van Moolenbroek 		free(src);
1197*e4e21ee1SDavid van Moolenbroek 		objects--;
1198*e4e21ee1SDavid van Moolenbroek 	}
1199*e4e21ee1SDavid van Moolenbroek 
1200*e4e21ee1SDavid van Moolenbroek 	return r;
1201*e4e21ee1SDavid van Moolenbroek }
1202*e4e21ee1SDavid van Moolenbroek 
1203*e4e21ee1SDavid van Moolenbroek /*
1204*e4e21ee1SDavid van Moolenbroek  * Read and/or write the value of a regular data node.  A regular data node is
1205*e4e21ee1SDavid van Moolenbroek  * a leaf node.  Typically, a leaf node has no associated function, in which
1206*e4e21ee1SDavid van Moolenbroek  * case this function will be used instead.  In addition, this function may be
1207*e4e21ee1SDavid van Moolenbroek  * used from handler functions as part of their functionality.
1208*e4e21ee1SDavid van Moolenbroek  */
1209*e4e21ee1SDavid van Moolenbroek ssize_t
1210*e4e21ee1SDavid van Moolenbroek mib_readwrite(struct mib_call * call, struct mib_node * node,
1211*e4e21ee1SDavid van Moolenbroek 	struct mib_oldp * oldp, struct mib_newp * newp, mib_verify_ptr verify)
1212*e4e21ee1SDavid van Moolenbroek {
1213*e4e21ee1SDavid van Moolenbroek 	ssize_t len;
1214*e4e21ee1SDavid van Moolenbroek 	int r;
1215*e4e21ee1SDavid van Moolenbroek 
1216*e4e21ee1SDavid van Moolenbroek 	/* Copy out old data, if requested.  Always get the old data length. */
1217*e4e21ee1SDavid van Moolenbroek 	if ((r = len = mib_read(node, oldp)) < 0)
1218*e4e21ee1SDavid van Moolenbroek 		return r;
1219*e4e21ee1SDavid van Moolenbroek 
1220*e4e21ee1SDavid van Moolenbroek 	/* Copy in new data, if requested. */
1221*e4e21ee1SDavid van Moolenbroek 	if ((r = mib_write(call, node, newp, verify)) != OK)
1222*e4e21ee1SDavid van Moolenbroek 		return r;
1223*e4e21ee1SDavid van Moolenbroek 
1224*e4e21ee1SDavid van Moolenbroek 	/* Return the old data length. */
1225*e4e21ee1SDavid van Moolenbroek 	return len;
1226*e4e21ee1SDavid van Moolenbroek }
1227*e4e21ee1SDavid van Moolenbroek 
1228*e4e21ee1SDavid van Moolenbroek /*
1229*e4e21ee1SDavid van Moolenbroek  * Dispatch a sysctl call, by looking up the target node by its MIB name and
1230*e4e21ee1SDavid van Moolenbroek  * taking the appropriate action on the resulting node, if found.  Return the
1231*e4e21ee1SDavid van Moolenbroek  * old data length on success, or a negative error code on failure.
1232*e4e21ee1SDavid van Moolenbroek  */
1233*e4e21ee1SDavid van Moolenbroek ssize_t
1234*e4e21ee1SDavid van Moolenbroek mib_dispatch(struct mib_call * call, struct mib_node * root,
1235*e4e21ee1SDavid van Moolenbroek 	struct mib_oldp * oldp, struct mib_newp * newp)
1236*e4e21ee1SDavid van Moolenbroek {
1237*e4e21ee1SDavid van Moolenbroek 	struct mib_node *stack[CTL_MAXNAME];
1238*e4e21ee1SDavid van Moolenbroek 	struct mib_node *parent, *node;
1239*e4e21ee1SDavid van Moolenbroek 	int id, depth, is_leaf, has_verify, has_func;
1240*e4e21ee1SDavid van Moolenbroek 
1241*e4e21ee1SDavid van Moolenbroek 	assert(call->call_namelen <= CTL_MAXNAME);
1242*e4e21ee1SDavid van Moolenbroek 
1243*e4e21ee1SDavid van Moolenbroek 	/*
1244*e4e21ee1SDavid van Moolenbroek 	 * Resolve the name by descending into the node tree, level by level,
1245*e4e21ee1SDavid van Moolenbroek 	 * starting at the MIB root.
1246*e4e21ee1SDavid van Moolenbroek 	 */
1247*e4e21ee1SDavid van Moolenbroek 	depth = 0;
1248*e4e21ee1SDavid van Moolenbroek 
1249*e4e21ee1SDavid van Moolenbroek 	for (parent = root; call->call_namelen > 0; parent = node) {
1250*e4e21ee1SDavid van Moolenbroek 		/*
1251*e4e21ee1SDavid van Moolenbroek 		 * For node creation and destruction, build a node stack, to
1252*e4e21ee1SDavid van Moolenbroek 		 * allow for up-propagation of new node version numbers.
1253*e4e21ee1SDavid van Moolenbroek 		 */
1254*e4e21ee1SDavid van Moolenbroek 		stack[depth++] = parent;
1255*e4e21ee1SDavid van Moolenbroek 
1256*e4e21ee1SDavid van Moolenbroek 		id = call->call_name[0];
1257*e4e21ee1SDavid van Moolenbroek 		call->call_name++;
1258*e4e21ee1SDavid van Moolenbroek 		call->call_namelen--;
1259*e4e21ee1SDavid van Moolenbroek 
1260*e4e21ee1SDavid van Moolenbroek 		assert(SYSCTL_TYPE(parent->node_flags) == CTLTYPE_NODE);
1261*e4e21ee1SDavid van Moolenbroek 		assert(parent->node_flags & CTLFLAG_PARENT);
1262*e4e21ee1SDavid van Moolenbroek 
1263*e4e21ee1SDavid van Moolenbroek 		/*
1264*e4e21ee1SDavid van Moolenbroek 		 * Check for meta-identifiers.  Regular identifiers are never
1265*e4e21ee1SDavid van Moolenbroek 		 * negative, although node handler functions may take subpaths
1266*e4e21ee1SDavid van Moolenbroek 		 * with negative identifiers that are not meta-identifiers
1267*e4e21ee1SDavid van Moolenbroek 		 * (e.g., see KERN_PROC2).
1268*e4e21ee1SDavid van Moolenbroek 		 */
1269*e4e21ee1SDavid van Moolenbroek 		if (id < 0) {
1270*e4e21ee1SDavid van Moolenbroek 			/*
1271*e4e21ee1SDavid van Moolenbroek 			 * A meta-identifier must always be the last name
1272*e4e21ee1SDavid van Moolenbroek 			 * component.
1273*e4e21ee1SDavid van Moolenbroek 			 */
1274*e4e21ee1SDavid van Moolenbroek 			if (call->call_namelen > 0)
1275*e4e21ee1SDavid van Moolenbroek 				return EINVAL;
1276*e4e21ee1SDavid van Moolenbroek 
1277*e4e21ee1SDavid van Moolenbroek 			switch (id) {
1278*e4e21ee1SDavid van Moolenbroek 			case CTL_QUERY:
1279*e4e21ee1SDavid van Moolenbroek 				return mib_query(call, parent, oldp, newp,
1280*e4e21ee1SDavid van Moolenbroek 				    root);
1281*e4e21ee1SDavid van Moolenbroek 			case CTL_CREATE:
1282*e4e21ee1SDavid van Moolenbroek 				return mib_create(call, parent, oldp, newp,
1283*e4e21ee1SDavid van Moolenbroek 				    stack, depth);
1284*e4e21ee1SDavid van Moolenbroek 			case CTL_DESTROY:
1285*e4e21ee1SDavid van Moolenbroek 				return mib_destroy(call, parent, oldp, newp,
1286*e4e21ee1SDavid van Moolenbroek 				    stack, depth);
1287*e4e21ee1SDavid van Moolenbroek 			case CTL_DESCRIBE:
1288*e4e21ee1SDavid van Moolenbroek 				return mib_describe(call, parent, oldp, newp);
1289*e4e21ee1SDavid van Moolenbroek 			case CTL_CREATESYM:
1290*e4e21ee1SDavid van Moolenbroek 			case CTL_MMAP:
1291*e4e21ee1SDavid van Moolenbroek 			default:
1292*e4e21ee1SDavid van Moolenbroek 				return EOPNOTSUPP;
1293*e4e21ee1SDavid van Moolenbroek 			}
1294*e4e21ee1SDavid van Moolenbroek 		}
1295*e4e21ee1SDavid van Moolenbroek 
1296*e4e21ee1SDavid van Moolenbroek 		/* Locate the child node. */
1297*e4e21ee1SDavid van Moolenbroek 		if ((node = mib_find(parent, id, NULL /*prevp*/)) == NULL)
1298*e4e21ee1SDavid van Moolenbroek 			return ENOENT;
1299*e4e21ee1SDavid van Moolenbroek 
1300*e4e21ee1SDavid van Moolenbroek 		/* Check if access is permitted at this level. */
1301*e4e21ee1SDavid van Moolenbroek 		if ((node->node_flags & CTLFLAG_PRIVATE) && !mib_authed(call))
1302*e4e21ee1SDavid van Moolenbroek 			return EPERM;
1303*e4e21ee1SDavid van Moolenbroek 
1304*e4e21ee1SDavid van Moolenbroek 		/*
1305*e4e21ee1SDavid van Moolenbroek 		 * Is this a leaf node, and/or is this node handled by a
1306*e4e21ee1SDavid van Moolenbroek 		 * function?  If either is true, resolution ends at this level.
1307*e4e21ee1SDavid van Moolenbroek 		 * In order to save a few bytes of memory per node, we use
1308*e4e21ee1SDavid van Moolenbroek 		 * different ways to determine whether there is a function
1309*e4e21ee1SDavid van Moolenbroek 		 * depending on whether the node is a leaf or not.
1310*e4e21ee1SDavid van Moolenbroek 		 */
1311*e4e21ee1SDavid van Moolenbroek 		is_leaf = (SYSCTL_TYPE(node->node_flags) != CTLTYPE_NODE);
1312*e4e21ee1SDavid van Moolenbroek 		if (is_leaf) {
1313*e4e21ee1SDavid van Moolenbroek 			has_verify = (node->node_flags & CTLFLAG_VERIFY);
1314*e4e21ee1SDavid van Moolenbroek 			has_func = (!has_verify && node->node_func != NULL);
1315*e4e21ee1SDavid van Moolenbroek 		} else {
1316*e4e21ee1SDavid van Moolenbroek 			has_verify = FALSE;
1317*e4e21ee1SDavid van Moolenbroek 			has_func = !(node->node_flags & CTLFLAG_PARENT);
1318*e4e21ee1SDavid van Moolenbroek 		}
1319*e4e21ee1SDavid van Moolenbroek 
1320*e4e21ee1SDavid van Moolenbroek 		/*
1321*e4e21ee1SDavid van Moolenbroek 		 * The name may be longer only if the node is not a leaf.  That
1322*e4e21ee1SDavid van Moolenbroek 		 * also applies to leaves with functions, so check this first.
1323*e4e21ee1SDavid van Moolenbroek 		 */
1324*e4e21ee1SDavid van Moolenbroek 		if (is_leaf && call->call_namelen > 0)
1325*e4e21ee1SDavid van Moolenbroek 			return ENOTDIR;
1326*e4e21ee1SDavid van Moolenbroek 
1327*e4e21ee1SDavid van Moolenbroek 		/*
1328*e4e21ee1SDavid van Moolenbroek 		 * If resolution indeed ends here, and the user supplied new
1329*e4e21ee1SDavid van Moolenbroek 		 * data, check if writing is allowed.  For functions, it is
1330*e4e21ee1SDavid van Moolenbroek 		 * arguable whether we should do this check here already.
1331*e4e21ee1SDavid van Moolenbroek 		 * However, for now, this approach covers all our use cases.
1332*e4e21ee1SDavid van Moolenbroek 		 */
1333*e4e21ee1SDavid van Moolenbroek 		if ((is_leaf || has_func) && newp != NULL) {
1334*e4e21ee1SDavid van Moolenbroek 			if (!(node->node_flags & CTLFLAG_READWRITE))
1335*e4e21ee1SDavid van Moolenbroek 				return EPERM;
1336*e4e21ee1SDavid van Moolenbroek 
1337*e4e21ee1SDavid van Moolenbroek 			/*
1338*e4e21ee1SDavid van Moolenbroek 			 * Unless nonprivileged users may write to this node,
1339*e4e21ee1SDavid van Moolenbroek 			 * ensure that the user has superuser privileges.  The
1340*e4e21ee1SDavid van Moolenbroek 			 * ANYWRITE flag does not override the READWRITE flag.
1341*e4e21ee1SDavid van Moolenbroek 			 */
1342*e4e21ee1SDavid van Moolenbroek 			if (!(node->node_flags & CTLFLAG_ANYWRITE) &&
1343*e4e21ee1SDavid van Moolenbroek 			    !mib_authed(call))
1344*e4e21ee1SDavid van Moolenbroek 				return EPERM;
1345*e4e21ee1SDavid van Moolenbroek 		}
1346*e4e21ee1SDavid van Moolenbroek 
1347*e4e21ee1SDavid van Moolenbroek 		/* If this node has a handler function, let it do the work. */
1348*e4e21ee1SDavid van Moolenbroek 		if (has_func)
1349*e4e21ee1SDavid van Moolenbroek 			return node->node_func(call, node, oldp, newp);
1350*e4e21ee1SDavid van Moolenbroek 
1351*e4e21ee1SDavid van Moolenbroek 		/* For regular data leaf nodes, handle generic access. */
1352*e4e21ee1SDavid van Moolenbroek 		if (is_leaf)
1353*e4e21ee1SDavid van Moolenbroek 			return mib_readwrite(call, node, oldp, newp,
1354*e4e21ee1SDavid van Moolenbroek 			    has_verify ? node->node_verify : NULL);
1355*e4e21ee1SDavid van Moolenbroek 
1356*e4e21ee1SDavid van Moolenbroek 		/* No function and not a leaf?  Descend further. */
1357*e4e21ee1SDavid van Moolenbroek 	}
1358*e4e21ee1SDavid van Moolenbroek 
1359*e4e21ee1SDavid van Moolenbroek 	/* If we get here, the name refers to a node array. */
1360*e4e21ee1SDavid van Moolenbroek 	return EISDIR;
1361*e4e21ee1SDavid van Moolenbroek }
1362*e4e21ee1SDavid van Moolenbroek 
1363*e4e21ee1SDavid van Moolenbroek /*
1364*e4e21ee1SDavid van Moolenbroek  * Recursively initialize the static tree at initialization time.
1365*e4e21ee1SDavid van Moolenbroek  */
1366*e4e21ee1SDavid van Moolenbroek static void
1367*e4e21ee1SDavid van Moolenbroek mib_tree_recurse(struct mib_node * parent)
1368*e4e21ee1SDavid van Moolenbroek {
1369*e4e21ee1SDavid van Moolenbroek 	struct mib_node *node;
1370*e4e21ee1SDavid van Moolenbroek 	int id;
1371*e4e21ee1SDavid van Moolenbroek 
1372*e4e21ee1SDavid van Moolenbroek 	assert(SYSCTL_TYPE(parent->node_flags) == CTLTYPE_NODE);
1373*e4e21ee1SDavid van Moolenbroek 	assert(parent->node_flags & CTLFLAG_PARENT);
1374*e4e21ee1SDavid van Moolenbroek 
1375*e4e21ee1SDavid van Moolenbroek 	/*
1376*e4e21ee1SDavid van Moolenbroek 	 * Later on, node_csize and node_clen will also include dynamically
1377*e4e21ee1SDavid van Moolenbroek 	 * created nodes.  This means that we cannot use node_csize to iterate
1378*e4e21ee1SDavid van Moolenbroek 	 * over the static nodes.
1379*e4e21ee1SDavid van Moolenbroek 	 */
1380*e4e21ee1SDavid van Moolenbroek 	parent->node_csize = parent->node_size;
1381*e4e21ee1SDavid van Moolenbroek 
1382*e4e21ee1SDavid van Moolenbroek 	node = parent->node_scptr;
1383*e4e21ee1SDavid van Moolenbroek 
1384*e4e21ee1SDavid van Moolenbroek 	for (id = 0; IS_STATIC_ID(parent, id); id++, node++) {
1385*e4e21ee1SDavid van Moolenbroek 		if (node->node_flags == 0)
1386*e4e21ee1SDavid van Moolenbroek 			continue;
1387*e4e21ee1SDavid van Moolenbroek 
1388*e4e21ee1SDavid van Moolenbroek 		nodes++;
1389*e4e21ee1SDavid van Moolenbroek 
1390*e4e21ee1SDavid van Moolenbroek 		parent->node_clen++;
1391*e4e21ee1SDavid van Moolenbroek 
1392*e4e21ee1SDavid van Moolenbroek 		node->node_ver = parent->node_ver;
1393*e4e21ee1SDavid van Moolenbroek 
1394*e4e21ee1SDavid van Moolenbroek 		/* Recursively apply this function to all node children. */
1395*e4e21ee1SDavid van Moolenbroek 		if (SYSCTL_TYPE(node->node_flags) == CTLTYPE_NODE &&
1396*e4e21ee1SDavid van Moolenbroek 		    (node->node_flags & CTLFLAG_PARENT))
1397*e4e21ee1SDavid van Moolenbroek 			mib_tree_recurse(node);
1398*e4e21ee1SDavid van Moolenbroek 	}
1399*e4e21ee1SDavid van Moolenbroek }
1400*e4e21ee1SDavid van Moolenbroek 
1401*e4e21ee1SDavid van Moolenbroek /*
1402*e4e21ee1SDavid van Moolenbroek  * Go through the entire static tree, recursively, initializing some values
1403*e4e21ee1SDavid van Moolenbroek  * that could not be assigned at compile time.
1404*e4e21ee1SDavid van Moolenbroek  */
1405*e4e21ee1SDavid van Moolenbroek void
1406*e4e21ee1SDavid van Moolenbroek mib_tree_init(struct mib_node * root)
1407*e4e21ee1SDavid van Moolenbroek {
1408*e4e21ee1SDavid van Moolenbroek 
1409*e4e21ee1SDavid van Moolenbroek 	/* Initialize some variables. */
1410*e4e21ee1SDavid van Moolenbroek 	nodes = 1; /* the root node itself */
1411*e4e21ee1SDavid van Moolenbroek 	objects = 0;
1412*e4e21ee1SDavid van Moolenbroek 
1413*e4e21ee1SDavid van Moolenbroek 	/* The entire tree starts with the same, nonzero node version. */
1414*e4e21ee1SDavid van Moolenbroek 	root->node_ver = 1;
1415*e4e21ee1SDavid van Moolenbroek 
1416*e4e21ee1SDavid van Moolenbroek 	/* Recursively initialize the static tree. */
1417*e4e21ee1SDavid van Moolenbroek 	mib_tree_recurse(root);
1418*e4e21ee1SDavid van Moolenbroek }
1419