1e4e21ee1SDavid van Moolenbroek /* MIB service - tree.c - tree access and management */
2e4e21ee1SDavid van Moolenbroek
3e4e21ee1SDavid van Moolenbroek #include "mib.h"
4e4e21ee1SDavid van Moolenbroek
5e4e21ee1SDavid van Moolenbroek /*
6e4e21ee1SDavid van Moolenbroek * Does the given identifier fall within the range of static identifiers in the
7e4e21ee1SDavid van Moolenbroek * given parent? This check can be used to enumerate all static array entries
8e4e21ee1SDavid van Moolenbroek * in the given parent, starting from zero. The check does not guarantee that
9e4e21ee1SDavid van Moolenbroek * the entry is actually for a valid node, nor does it guarantee that there is
10e4e21ee1SDavid van Moolenbroek * not a dynamic node with this identifier.
11e4e21ee1SDavid van Moolenbroek */
12e4e21ee1SDavid van Moolenbroek #define IS_STATIC_ID(parent, id) ((unsigned int)(id) < (parent)->node_size)
13e4e21ee1SDavid van Moolenbroek
14e4e21ee1SDavid van Moolenbroek /*
15e4e21ee1SDavid van Moolenbroek * Scratch buffer, used for various cases of temporary data storage. It must
16e4e21ee1SDavid van Moolenbroek * be large enough to fit a sysctldesc structure followed by the longest
17e4e21ee1SDavid van Moolenbroek * supported description. It must also be large enough to serve as temporary
18e4e21ee1SDavid van Moolenbroek * storage for data being written in the majority of cases. Finally, it must
19e4e21ee1SDavid van Moolenbroek * be large enough to contain an entire page, for mib_copyin_str().
20e4e21ee1SDavid van Moolenbroek */
21e4e21ee1SDavid van Moolenbroek #define MAXDESCLEN 1024 /* from NetBSD */
22e4e21ee1SDavid van Moolenbroek #define SCRATCH_SIZE MAX(PAGE_SIZE, sizeof(struct sysctldesc) + MAXDESCLEN)
23e4e21ee1SDavid van Moolenbroek static char scratch[SCRATCH_SIZE] __aligned(sizeof(int32_t));
24e4e21ee1SDavid van Moolenbroek
25*6f3e0bcdSDavid van Moolenbroek unsigned int mib_nodes; /* how many nodes are there in the tree? */
26*6f3e0bcdSDavid van Moolenbroek unsigned int mib_objects; /* how many memory objects are allocated? */
27*6f3e0bcdSDavid van Moolenbroek unsigned int mib_remotes; /* how many remote subtrees are there? */
28e4e21ee1SDavid van Moolenbroek
29e4e21ee1SDavid van Moolenbroek /*
30e4e21ee1SDavid van Moolenbroek * Find a node through its parent node and identifier. Return the node if it
31e4e21ee1SDavid van Moolenbroek * was found, and optionally store a pointer to the pointer to its dynode
32e4e21ee1SDavid van Moolenbroek * superstructure (for removal). If no matching node was found, return NULL.
33e4e21ee1SDavid van Moolenbroek */
34e4e21ee1SDavid van Moolenbroek static struct mib_node *
mib_find(struct mib_node * parent,int id,struct mib_dynode *** prevpp)35e4e21ee1SDavid van Moolenbroek mib_find(struct mib_node * parent, int id, struct mib_dynode *** prevpp)
36e4e21ee1SDavid van Moolenbroek {
37e4e21ee1SDavid van Moolenbroek struct mib_node *node;
38e4e21ee1SDavid van Moolenbroek struct mib_dynode **dynp;
39e4e21ee1SDavid van Moolenbroek
40e4e21ee1SDavid van Moolenbroek if (id < 0)
41e4e21ee1SDavid van Moolenbroek return NULL;
42e4e21ee1SDavid van Moolenbroek
43e4e21ee1SDavid van Moolenbroek /*
44e4e21ee1SDavid van Moolenbroek * Is there a static node with this identifier? The static nodes are
45e4e21ee1SDavid van Moolenbroek * all in a single array, so lookup is O(1) for these nodes. We use
46e4e21ee1SDavid van Moolenbroek * the node flags field to see whether the array entry is valid.
47e4e21ee1SDavid van Moolenbroek */
48e4e21ee1SDavid van Moolenbroek if (IS_STATIC_ID(parent, id)) {
49e4e21ee1SDavid van Moolenbroek node = &parent->node_scptr[id];
50e4e21ee1SDavid van Moolenbroek
51e4e21ee1SDavid van Moolenbroek if (node->node_flags != 0) {
52e4e21ee1SDavid van Moolenbroek /* Found a matching static node. */
53e4e21ee1SDavid van Moolenbroek if (prevpp != NULL)
54e4e21ee1SDavid van Moolenbroek *prevpp = NULL;
55e4e21ee1SDavid van Moolenbroek return node;
56e4e21ee1SDavid van Moolenbroek }
57e4e21ee1SDavid van Moolenbroek }
58e4e21ee1SDavid van Moolenbroek
59e4e21ee1SDavid van Moolenbroek /*
60e4e21ee1SDavid van Moolenbroek * Is there a dynamic node with this identifier? The dynamic nodes
61e4e21ee1SDavid van Moolenbroek * form a linked list. This is predominantly because userland may pick
62e4e21ee1SDavid van Moolenbroek * the identifier number at creation time, so we cannot rely on all
63e4e21ee1SDavid van Moolenbroek * dynamically created nodes falling into a small identifier range.
64e4e21ee1SDavid van Moolenbroek * That eliminates the option of a dynamic array indexed by identifier,
65e4e21ee1SDavid van Moolenbroek * and a linked list is the simplest next option. Thus, dynamic node
66e4e21ee1SDavid van Moolenbroek * lookup is O(n). However, since the list is sorted by identifier,
67e4e21ee1SDavid van Moolenbroek * we may be able to stop the search early.
68e4e21ee1SDavid van Moolenbroek */
69e4e21ee1SDavid van Moolenbroek for (dynp = &parent->node_dcptr; *dynp != NULL;
70e4e21ee1SDavid van Moolenbroek dynp = &((*dynp)->dynode_next)) {
71e4e21ee1SDavid van Moolenbroek if ((*dynp)->dynode_id == id) {
72e4e21ee1SDavid van Moolenbroek /* Found a matching dynamic node. */
73e4e21ee1SDavid van Moolenbroek if (prevpp != NULL)
74e4e21ee1SDavid van Moolenbroek *prevpp = dynp;
75e4e21ee1SDavid van Moolenbroek return &(*dynp)->dynode_node;
76e4e21ee1SDavid van Moolenbroek } else if ((*dynp)->dynode_id > id)
77e4e21ee1SDavid van Moolenbroek break; /* no need to look further */
78e4e21ee1SDavid van Moolenbroek }
79e4e21ee1SDavid van Moolenbroek
80e4e21ee1SDavid van Moolenbroek return NULL;
81e4e21ee1SDavid van Moolenbroek }
82e4e21ee1SDavid van Moolenbroek
83e4e21ee1SDavid van Moolenbroek /*
84e4e21ee1SDavid van Moolenbroek * Copy out a node to userland, using the exchange format for nodes (namely,
85e4e21ee1SDavid van Moolenbroek * a sysctlnode structure). Return the size of the object that is (or, if the
86e4e21ee1SDavid van Moolenbroek * node falls outside the requested data range, would be) copied out on
87e4e21ee1SDavid van Moolenbroek * success, or a negative error code on failure. The function may return 0
88e4e21ee1SDavid van Moolenbroek * to indicate that nothing was copied out after all (this is unused here).
89e4e21ee1SDavid van Moolenbroek */
90e4e21ee1SDavid van Moolenbroek static ssize_t
mib_copyout_node(struct mib_call * call,struct mib_oldp * oldp,size_t off,int id,const struct mib_node * node)91e4e21ee1SDavid van Moolenbroek mib_copyout_node(struct mib_call * call, struct mib_oldp * oldp, size_t off,
92e4e21ee1SDavid van Moolenbroek int id, const struct mib_node * node)
93e4e21ee1SDavid van Moolenbroek {
94e4e21ee1SDavid van Moolenbroek struct sysctlnode scn;
95e4e21ee1SDavid van Moolenbroek int visible;
96e4e21ee1SDavid van Moolenbroek
97e4e21ee1SDavid van Moolenbroek if (!mib_inrange(oldp, off))
98e4e21ee1SDavid van Moolenbroek return sizeof(scn); /* nothing to do */
99e4e21ee1SDavid van Moolenbroek
100e4e21ee1SDavid van Moolenbroek memset(&scn, 0, sizeof(scn));
101e4e21ee1SDavid van Moolenbroek
102e4e21ee1SDavid van Moolenbroek /*
103*6f3e0bcdSDavid van Moolenbroek * We use CTLFLAG_PARENT, CTLFLAG_VERIFY, and CTLFLAG_REMOTE internally
104*6f3e0bcdSDavid van Moolenbroek * only. NetBSD uses the values of these flags for different purposes.
105*6f3e0bcdSDavid van Moolenbroek * Either way, do not expose them to userland.
106e4e21ee1SDavid van Moolenbroek */
107*6f3e0bcdSDavid van Moolenbroek scn.sysctl_flags = SYSCTL_VERSION | (node->node_flags &
108*6f3e0bcdSDavid van Moolenbroek ~(CTLFLAG_PARENT | CTLFLAG_VERIFY | CTLFLAG_REMOTE));
109e4e21ee1SDavid van Moolenbroek scn.sysctl_num = id;
110e4e21ee1SDavid van Moolenbroek strlcpy(scn.sysctl_name, node->node_name, sizeof(scn.sysctl_name));
111e4e21ee1SDavid van Moolenbroek scn.sysctl_ver = node->node_ver;
112e4e21ee1SDavid van Moolenbroek scn.sysctl_size = node->node_size;
113e4e21ee1SDavid van Moolenbroek
114e4e21ee1SDavid van Moolenbroek /* Some information is only visible if the user can access the node. */
115e4e21ee1SDavid van Moolenbroek visible = (!(node->node_flags & CTLFLAG_PRIVATE) || mib_authed(call));
116e4e21ee1SDavid van Moolenbroek
117e4e21ee1SDavid van Moolenbroek /*
118e4e21ee1SDavid van Moolenbroek * For immediate types, store the immediate value in the resulting
119e4e21ee1SDavid van Moolenbroek * structure, unless the caller is not authorized to obtain the value.
120e4e21ee1SDavid van Moolenbroek */
121e4e21ee1SDavid van Moolenbroek if ((node->node_flags & CTLFLAG_IMMEDIATE) && visible) {
122e4e21ee1SDavid van Moolenbroek switch (SYSCTL_TYPE(node->node_flags)) {
123e4e21ee1SDavid van Moolenbroek case CTLTYPE_BOOL:
124e4e21ee1SDavid van Moolenbroek scn.sysctl_bdata = node->node_bool;
125e4e21ee1SDavid van Moolenbroek break;
126e4e21ee1SDavid van Moolenbroek case CTLTYPE_INT:
127e4e21ee1SDavid van Moolenbroek scn.sysctl_idata = node->node_int;
128e4e21ee1SDavid van Moolenbroek break;
129e4e21ee1SDavid van Moolenbroek case CTLTYPE_QUAD:
130e4e21ee1SDavid van Moolenbroek scn.sysctl_qdata = node->node_quad;
131e4e21ee1SDavid van Moolenbroek }
132e4e21ee1SDavid van Moolenbroek }
133e4e21ee1SDavid van Moolenbroek
134e4e21ee1SDavid van Moolenbroek /* Special rules apply to parent nodes. */
135e4e21ee1SDavid van Moolenbroek if (SYSCTL_TYPE(node->node_flags) == CTLTYPE_NODE) {
136e4e21ee1SDavid van Moolenbroek /* Report the node size the way NetBSD does, just in case. */
137e4e21ee1SDavid van Moolenbroek scn.sysctl_size = sizeof(scn);
138e4e21ee1SDavid van Moolenbroek
139e4e21ee1SDavid van Moolenbroek /*
140*6f3e0bcdSDavid van Moolenbroek * If this is a remote node, use the values we have of the root
141*6f3e0bcdSDavid van Moolenbroek * of the remote subtree. If we did not have these values, we
142*6f3e0bcdSDavid van Moolenbroek * would have to call into the remote service here, which for
143*6f3e0bcdSDavid van Moolenbroek * reliability purposes is a bad idea.
144*6f3e0bcdSDavid van Moolenbroek *
145*6f3e0bcdSDavid van Moolenbroek * If this is a real parent node, report child information. In
146*6f3e0bcdSDavid van Moolenbroek * both these cases, expose child information only if the node
147*6f3e0bcdSDavid van Moolenbroek * itself is accessible by the caller.
148*6f3e0bcdSDavid van Moolenbroek *
149e4e21ee1SDavid van Moolenbroek * If this is a function-driven node, indicate this by setting
150e4e21ee1SDavid van Moolenbroek * a nonzero function address. This allows trace(1) to
151e4e21ee1SDavid van Moolenbroek * determine that it should not attempt to descend into this
152e4e21ee1SDavid van Moolenbroek * part of the tree as usual, because a) accessing subnodes may
153e4e21ee1SDavid van Moolenbroek * have side effects, and b) meta-identifiers may not work as
154e4e21ee1SDavid van Moolenbroek * expected in these parts of the tree. Do not return the real
155e4e21ee1SDavid van Moolenbroek * function pointer, as this would leak anti-ASR information.
156e4e21ee1SDavid van Moolenbroek */
157*6f3e0bcdSDavid van Moolenbroek if (node->node_flags & CTLFLAG_REMOTE) {
158*6f3e0bcdSDavid van Moolenbroek if (visible) {
159*6f3e0bcdSDavid van Moolenbroek scn.sysctl_csize = node->node_rcsize;
160*6f3e0bcdSDavid van Moolenbroek scn.sysctl_clen = node->node_rclen;
161*6f3e0bcdSDavid van Moolenbroek }
162*6f3e0bcdSDavid van Moolenbroek } else if (node->node_flags & CTLFLAG_PARENT) {
163*6f3e0bcdSDavid van Moolenbroek if (visible) {
164*6f3e0bcdSDavid van Moolenbroek scn.sysctl_csize = node->node_csize;
165*6f3e0bcdSDavid van Moolenbroek scn.sysctl_clen = node->node_clen;
166*6f3e0bcdSDavid van Moolenbroek }
167*6f3e0bcdSDavid van Moolenbroek } else
168e4e21ee1SDavid van Moolenbroek scn.sysctl_func = SYSCTL_NODE_FN;
169e4e21ee1SDavid van Moolenbroek }
170e4e21ee1SDavid van Moolenbroek
171e4e21ee1SDavid van Moolenbroek /* Copy out the resulting node. */
172e4e21ee1SDavid van Moolenbroek return mib_copyout(oldp, off, &scn, sizeof(scn));
173e4e21ee1SDavid van Moolenbroek }
174e4e21ee1SDavid van Moolenbroek
175e4e21ee1SDavid van Moolenbroek /*
176e4e21ee1SDavid van Moolenbroek * Given a query on a non-leaf (parent) node, provide the user with an array of
177e4e21ee1SDavid van Moolenbroek * this node's children.
178e4e21ee1SDavid van Moolenbroek */
179e4e21ee1SDavid van Moolenbroek static ssize_t
mib_query(struct mib_call * call,struct mib_node * parent,struct mib_oldp * oldp,struct mib_newp * newp)180e4e21ee1SDavid van Moolenbroek mib_query(struct mib_call * call, struct mib_node * parent,
181*6f3e0bcdSDavid van Moolenbroek struct mib_oldp * oldp, struct mib_newp * newp)
182e4e21ee1SDavid van Moolenbroek {
183e4e21ee1SDavid van Moolenbroek struct sysctlnode scn;
184e4e21ee1SDavid van Moolenbroek struct mib_node *node;
185e4e21ee1SDavid van Moolenbroek struct mib_dynode *dynode;
186e4e21ee1SDavid van Moolenbroek size_t off;
187e4e21ee1SDavid van Moolenbroek int r, id;
188e4e21ee1SDavid van Moolenbroek
189e4e21ee1SDavid van Moolenbroek /* If the user passed in version numbers, check them. */
190e4e21ee1SDavid van Moolenbroek if (newp != NULL) {
191e4e21ee1SDavid van Moolenbroek if ((r = mib_copyin(newp, &scn, sizeof(scn))) != OK)
192e4e21ee1SDavid van Moolenbroek return r;
193e4e21ee1SDavid van Moolenbroek
194e4e21ee1SDavid van Moolenbroek if (SYSCTL_VERS(scn.sysctl_flags) != SYSCTL_VERSION)
195e4e21ee1SDavid van Moolenbroek return EINVAL;
196e4e21ee1SDavid van Moolenbroek
197e4e21ee1SDavid van Moolenbroek /*
198e4e21ee1SDavid van Moolenbroek * If a node version number is given, it must match the version
199e4e21ee1SDavid van Moolenbroek * of the parent or the root.
200e4e21ee1SDavid van Moolenbroek */
201*6f3e0bcdSDavid van Moolenbroek if (scn.sysctl_ver != 0 &&
202*6f3e0bcdSDavid van Moolenbroek scn.sysctl_ver != mib_root.node_ver &&
203e4e21ee1SDavid van Moolenbroek scn.sysctl_ver != parent->node_ver)
204e4e21ee1SDavid van Moolenbroek return EINVAL;
205e4e21ee1SDavid van Moolenbroek }
206e4e21ee1SDavid van Moolenbroek
207e4e21ee1SDavid van Moolenbroek /*
208e4e21ee1SDavid van Moolenbroek * We need not return the nodes strictly in ascending order of
209e4e21ee1SDavid van Moolenbroek * identifiers, as this is not expected by userland. For example,
210e4e21ee1SDavid van Moolenbroek * sysctlgetmibinfo(3) performs its own sorting after a query.
211e4e21ee1SDavid van Moolenbroek * Thus, we can go through the static and dynamic nodes separately.
212e4e21ee1SDavid van Moolenbroek */
213e4e21ee1SDavid van Moolenbroek off = 0;
214e4e21ee1SDavid van Moolenbroek
215e4e21ee1SDavid van Moolenbroek /* First enumerate the static nodes. */
216e4e21ee1SDavid van Moolenbroek for (id = 0; IS_STATIC_ID(parent, id); id++) {
217e4e21ee1SDavid van Moolenbroek node = &parent->node_scptr[id];
218e4e21ee1SDavid van Moolenbroek
219e4e21ee1SDavid van Moolenbroek if (node->node_flags == 0)
220e4e21ee1SDavid van Moolenbroek continue;
221e4e21ee1SDavid van Moolenbroek
222e4e21ee1SDavid van Moolenbroek if ((r = mib_copyout_node(call, oldp, off, id, node)) < 0)
223e4e21ee1SDavid van Moolenbroek return r;
224e4e21ee1SDavid van Moolenbroek off += r;
225e4e21ee1SDavid van Moolenbroek }
226e4e21ee1SDavid van Moolenbroek
227e4e21ee1SDavid van Moolenbroek /* Then enumerate the dynamic nodes. */
228e4e21ee1SDavid van Moolenbroek for (dynode = parent->node_dcptr; dynode != NULL;
229e4e21ee1SDavid van Moolenbroek dynode = dynode->dynode_next) {
230e4e21ee1SDavid van Moolenbroek node = &dynode->dynode_node;
231e4e21ee1SDavid van Moolenbroek
232e4e21ee1SDavid van Moolenbroek if ((r = mib_copyout_node(call, oldp, off, dynode->dynode_id,
233e4e21ee1SDavid van Moolenbroek node)) < 0)
234e4e21ee1SDavid van Moolenbroek return r;
235e4e21ee1SDavid van Moolenbroek off += r;
236e4e21ee1SDavid van Moolenbroek }
237e4e21ee1SDavid van Moolenbroek
238e4e21ee1SDavid van Moolenbroek return off;
239e4e21ee1SDavid van Moolenbroek }
240e4e21ee1SDavid van Moolenbroek
241e4e21ee1SDavid van Moolenbroek /*
242*6f3e0bcdSDavid van Moolenbroek * Check whether the given name buffer contains a valid node name string. If
243*6f3e0bcdSDavid van Moolenbroek * the name is nonempty, properly terminated, and contains only acceptable
244*6f3e0bcdSDavid van Moolenbroek * characters, return the length of the string excluding null terminator.
245*6f3e0bcdSDavid van Moolenbroek * Otherwise, return zero to indicate failure.
246*6f3e0bcdSDavid van Moolenbroek */
247*6f3e0bcdSDavid van Moolenbroek static size_t
mib_check_name(const char * name,size_t namesize)248*6f3e0bcdSDavid van Moolenbroek mib_check_name(const char * name, size_t namesize)
249*6f3e0bcdSDavid van Moolenbroek {
250*6f3e0bcdSDavid van Moolenbroek size_t namelen;
251*6f3e0bcdSDavid van Moolenbroek char c;
252*6f3e0bcdSDavid van Moolenbroek
253*6f3e0bcdSDavid van Moolenbroek /* Names must be nonempty, null terminated, C symbol style strings. */
254*6f3e0bcdSDavid van Moolenbroek for (namelen = 0; namelen < namesize; namelen++) {
255*6f3e0bcdSDavid van Moolenbroek if ((c = name[namelen]) == '\0')
256*6f3e0bcdSDavid van Moolenbroek break;
257*6f3e0bcdSDavid van Moolenbroek /* A-Z, a-z, 0-9, _ only, and no digit as first character. */
258*6f3e0bcdSDavid van Moolenbroek if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
259*6f3e0bcdSDavid van Moolenbroek c == '_' || (c >= '0' && c <= '9' && namelen > 0)))
260*6f3e0bcdSDavid van Moolenbroek return 0;
261*6f3e0bcdSDavid van Moolenbroek }
262*6f3e0bcdSDavid van Moolenbroek if (namelen == 0 || namelen == namesize)
263*6f3e0bcdSDavid van Moolenbroek return 0;
264*6f3e0bcdSDavid van Moolenbroek
265*6f3e0bcdSDavid van Moolenbroek return namelen;
266*6f3e0bcdSDavid van Moolenbroek }
267*6f3e0bcdSDavid van Moolenbroek
268*6f3e0bcdSDavid van Moolenbroek /*
269e4e21ee1SDavid van Moolenbroek * Scan a parent node's children, as part of new node creation. Search for
270e4e21ee1SDavid van Moolenbroek * either a free node identifier (if given_id < 0) or collisions with the node
271e4e21ee1SDavid van Moolenbroek * identifier to use (if given_id >= 0). Also check for name collisions. Upon
272e4e21ee1SDavid van Moolenbroek * success, return OK, with the resulting node identifier stored in 'idp' and a
273e4e21ee1SDavid van Moolenbroek * pointer to the pointer for the new dynamic node stored in 'prevpp'. Upon
274e4e21ee1SDavid van Moolenbroek * failure, return an error code. If the failure is EEXIST, 'idp' will contain
275e4e21ee1SDavid van Moolenbroek * the ID of the conflicting node, and 'nodep' will point to the node.
276e4e21ee1SDavid van Moolenbroek */
277e4e21ee1SDavid van Moolenbroek static int
mib_scan(struct mib_node * parent,int given_id,const char * name,int * idp,struct mib_dynode *** prevpp,struct mib_node ** nodep)278e4e21ee1SDavid van Moolenbroek mib_scan(struct mib_node * parent, int given_id, const char * name, int * idp,
279e4e21ee1SDavid van Moolenbroek struct mib_dynode *** prevpp, struct mib_node ** nodep)
280e4e21ee1SDavid van Moolenbroek {
281e4e21ee1SDavid van Moolenbroek struct mib_dynode **prevp, **dynp;
282e4e21ee1SDavid van Moolenbroek struct mib_node *node;
283e4e21ee1SDavid van Moolenbroek int id;
284e4e21ee1SDavid van Moolenbroek
285e4e21ee1SDavid van Moolenbroek /*
286e4e21ee1SDavid van Moolenbroek * We must verify that no entry already exists with the given name. In
287e4e21ee1SDavid van Moolenbroek * addition, if a nonnegative identifier is given, we should use that
288e4e21ee1SDavid van Moolenbroek * identifier and make sure it does not already exist. Otherwise, we
289e4e21ee1SDavid van Moolenbroek * must find a free identifier. Finally, we sort the dynamic nodes in
290e4e21ee1SDavid van Moolenbroek * ascending identifier order, so we must find the right place at which
291e4e21ee1SDavid van Moolenbroek * to insert the new node.
292e4e21ee1SDavid van Moolenbroek *
293e4e21ee1SDavid van Moolenbroek * For finding a free identifier, choose an ID that falls (well)
294e4e21ee1SDavid van Moolenbroek * outside the static range, both to avoid accidental retrieval by an
295e4e21ee1SDavid van Moolenbroek * application that uses a static ID, and to simplify verifying that
296e4e21ee1SDavid van Moolenbroek * the ID is indeed free. The sorting of dynamic nodes by identifier
297e4e21ee1SDavid van Moolenbroek * ensures that searching for a free identifier is O(n).
298e4e21ee1SDavid van Moolenbroek *
299e4e21ee1SDavid van Moolenbroek * At this time, we do not support some NetBSD features. We do not
300e4e21ee1SDavid van Moolenbroek * force success if the new node is sufficiently like an existing one.
301e4e21ee1SDavid van Moolenbroek * Also, we do not use global autoincrement for dynamic identifiers,
302e4e21ee1SDavid van Moolenbroek * although that could easily be changed.
303e4e21ee1SDavid van Moolenbroek */
304e4e21ee1SDavid van Moolenbroek
305e4e21ee1SDavid van Moolenbroek /* First check the static node array, just for collisions. */
306e4e21ee1SDavid van Moolenbroek for (id = 0; IS_STATIC_ID(parent, id); id++) {
307e4e21ee1SDavid van Moolenbroek node = &parent->node_scptr[id];
308e4e21ee1SDavid van Moolenbroek if (node->node_flags == 0)
309e4e21ee1SDavid van Moolenbroek continue;
310e4e21ee1SDavid van Moolenbroek if (id == given_id || !strcmp(name, node->node_name)) {
311e4e21ee1SDavid van Moolenbroek *idp = id;
312e4e21ee1SDavid van Moolenbroek *nodep = node;
313e4e21ee1SDavid van Moolenbroek return EEXIST;
314e4e21ee1SDavid van Moolenbroek }
315e4e21ee1SDavid van Moolenbroek }
316e4e21ee1SDavid van Moolenbroek
317e4e21ee1SDavid van Moolenbroek /*
318e4e21ee1SDavid van Moolenbroek * Then try to find the place to insert a new dynamic node. At the
319e4e21ee1SDavid van Moolenbroek * same time, check for both identifier and name collisions.
320e4e21ee1SDavid van Moolenbroek */
321e4e21ee1SDavid van Moolenbroek if (given_id >= 0)
322e4e21ee1SDavid van Moolenbroek id = given_id;
323e4e21ee1SDavid van Moolenbroek else
324e4e21ee1SDavid van Moolenbroek id = MAX(CREATE_BASE, parent->node_size);
325e4e21ee1SDavid van Moolenbroek
326e4e21ee1SDavid van Moolenbroek for (prevp = &parent->node_dcptr; *prevp != NULL;
327e4e21ee1SDavid van Moolenbroek prevp = &((*prevp)->dynode_next)) {
328e4e21ee1SDavid van Moolenbroek if ((*prevp)->dynode_id > id)
329e4e21ee1SDavid van Moolenbroek break;
330e4e21ee1SDavid van Moolenbroek if ((*prevp)->dynode_id == id) {
331e4e21ee1SDavid van Moolenbroek if (given_id >= 0) {
332e4e21ee1SDavid van Moolenbroek *idp = id;
333e4e21ee1SDavid van Moolenbroek *nodep = &(*prevp)->dynode_node;
334e4e21ee1SDavid van Moolenbroek return EEXIST;
335e4e21ee1SDavid van Moolenbroek } else
336e4e21ee1SDavid van Moolenbroek id++;
337e4e21ee1SDavid van Moolenbroek }
338e4e21ee1SDavid van Moolenbroek if (!strcmp(name, (*prevp)->dynode_node.node_name)) {
339e4e21ee1SDavid van Moolenbroek *idp = (*prevp)->dynode_id;
340e4e21ee1SDavid van Moolenbroek *nodep = &(*prevp)->dynode_node;
341e4e21ee1SDavid van Moolenbroek return EEXIST;
342e4e21ee1SDavid van Moolenbroek }
343e4e21ee1SDavid van Moolenbroek }
344e4e21ee1SDavid van Moolenbroek
345e4e21ee1SDavid van Moolenbroek /* Finally, check the rest of the dynamic nodes for name collisions. */
346e4e21ee1SDavid van Moolenbroek for (dynp = prevp; *dynp != NULL; dynp = &((*dynp)->dynode_next)) {
347e4e21ee1SDavid van Moolenbroek assert((*dynp)->dynode_id > id);
348e4e21ee1SDavid van Moolenbroek
349e4e21ee1SDavid van Moolenbroek if (!strcmp(name, (*dynp)->dynode_node.node_name)) {
350e4e21ee1SDavid van Moolenbroek *idp = (*dynp)->dynode_id;
351e4e21ee1SDavid van Moolenbroek *nodep = &(*dynp)->dynode_node;
352e4e21ee1SDavid van Moolenbroek return EEXIST;
353e4e21ee1SDavid van Moolenbroek }
354e4e21ee1SDavid van Moolenbroek }
355e4e21ee1SDavid van Moolenbroek
356e4e21ee1SDavid van Moolenbroek *idp = id;
357e4e21ee1SDavid van Moolenbroek *prevpp = prevp;
358e4e21ee1SDavid van Moolenbroek return OK;
359e4e21ee1SDavid van Moolenbroek }
360e4e21ee1SDavid van Moolenbroek
361e4e21ee1SDavid van Moolenbroek /*
362e4e21ee1SDavid van Moolenbroek * Copy in a string from the user process, located at the given remote address,
363e4e21ee1SDavid van Moolenbroek * into the given local buffer. If no buffer is given, just compute the length
364e4e21ee1SDavid van Moolenbroek * of the string. On success, return OK. If 'sizep' is not NULL, it will be
365e4e21ee1SDavid van Moolenbroek * filled with the string size, including the null terminator. If a non-NULL
366e4e21ee1SDavid van Moolenbroek * buffer was given, the string will be copied into the provided buffer (also
367e4e21ee1SDavid van Moolenbroek * including null terminator). Return an error code on failure, which includes
368e4e21ee1SDavid van Moolenbroek * the case that no null terminator was found within the range of bytes that
369e4e21ee1SDavid van Moolenbroek * would fit in the given buffer.
370e4e21ee1SDavid van Moolenbroek */
371e4e21ee1SDavid van Moolenbroek static int
mib_copyin_str(struct mib_newp * __restrict newp,vir_bytes addr,char * __restrict buf,size_t bufsize,size_t * __restrict sizep)372e4e21ee1SDavid van Moolenbroek mib_copyin_str(struct mib_newp * __restrict newp, vir_bytes addr,
373e4e21ee1SDavid van Moolenbroek char * __restrict buf, size_t bufsize, size_t * __restrict sizep)
374e4e21ee1SDavid van Moolenbroek {
375e4e21ee1SDavid van Moolenbroek char *ptr, *endp;
376e4e21ee1SDavid van Moolenbroek size_t chunk, len;
377e4e21ee1SDavid van Moolenbroek int r;
378e4e21ee1SDavid van Moolenbroek
379e4e21ee1SDavid van Moolenbroek assert(newp != NULL);
380e4e21ee1SDavid van Moolenbroek assert(bufsize <= SSIZE_MAX);
381e4e21ee1SDavid van Moolenbroek
382e4e21ee1SDavid van Moolenbroek if (addr == 0)
383e4e21ee1SDavid van Moolenbroek return EINVAL;
384e4e21ee1SDavid van Moolenbroek
385e4e21ee1SDavid van Moolenbroek /*
386e4e21ee1SDavid van Moolenbroek * NetBSD has a kernel routine for copying in a string from userland.
387e4e21ee1SDavid van Moolenbroek * MINIX3 does not, since its system call interface has always relied
388e4e21ee1SDavid van Moolenbroek * on userland passing in string lengths. The sysctl(2) API does not
389e4e21ee1SDavid van Moolenbroek * provide the string length, and thus, we have to do a bit of guess
390e4e21ee1SDavid van Moolenbroek * work. If we copy too little at once, performance suffers. If we
391e4e21ee1SDavid van Moolenbroek * copy too much at once, we may trigger an unneeded page fault. Make
392e4e21ee1SDavid van Moolenbroek * use of page boundaries to strike a balance between those two. If we
393e4e21ee1SDavid van Moolenbroek * are requested to just get the string length, use the scratch buffer.
394e4e21ee1SDavid van Moolenbroek */
395e4e21ee1SDavid van Moolenbroek len = 0;
396e4e21ee1SDavid van Moolenbroek
397e4e21ee1SDavid van Moolenbroek while (bufsize > 0) {
398e4e21ee1SDavid van Moolenbroek chunk = PAGE_SIZE - (addr % PAGE_SIZE);
399e4e21ee1SDavid van Moolenbroek if (chunk > bufsize)
400e4e21ee1SDavid van Moolenbroek chunk = bufsize;
401e4e21ee1SDavid van Moolenbroek
402e4e21ee1SDavid van Moolenbroek ptr = (buf != NULL) ? &buf[len] : scratch;
403e4e21ee1SDavid van Moolenbroek if ((r = mib_copyin_aux(newp, addr, ptr, chunk)) != OK)
404e4e21ee1SDavid van Moolenbroek return r;
405e4e21ee1SDavid van Moolenbroek
406e4e21ee1SDavid van Moolenbroek if ((endp = memchr(ptr, '\0', chunk)) != NULL) {
407e4e21ee1SDavid van Moolenbroek /* A null terminator was found - success. */
408e4e21ee1SDavid van Moolenbroek if (sizep != NULL)
409e4e21ee1SDavid van Moolenbroek *sizep = len + (size_t)(endp - ptr) + 1;
410e4e21ee1SDavid van Moolenbroek return OK;
411e4e21ee1SDavid van Moolenbroek }
412e4e21ee1SDavid van Moolenbroek
413e4e21ee1SDavid van Moolenbroek addr += chunk;
414e4e21ee1SDavid van Moolenbroek len += chunk;
415e4e21ee1SDavid van Moolenbroek bufsize -= chunk;
416e4e21ee1SDavid van Moolenbroek }
417e4e21ee1SDavid van Moolenbroek
418e4e21ee1SDavid van Moolenbroek /* No null terminator found. */
419e4e21ee1SDavid van Moolenbroek return EINVAL;
420e4e21ee1SDavid van Moolenbroek }
421e4e21ee1SDavid van Moolenbroek
422e4e21ee1SDavid van Moolenbroek /*
423e4e21ee1SDavid van Moolenbroek * Increase the version of the root node, and copy this new version to all
424*6f3e0bcdSDavid van Moolenbroek * nodes on the path to the given node, including that node itself.
425e4e21ee1SDavid van Moolenbroek */
426e4e21ee1SDavid van Moolenbroek static void
mib_upgrade(struct mib_node * node)427*6f3e0bcdSDavid van Moolenbroek mib_upgrade(struct mib_node * node)
428e4e21ee1SDavid van Moolenbroek {
429e4e21ee1SDavid van Moolenbroek uint32_t ver;
430e4e21ee1SDavid van Moolenbroek
431*6f3e0bcdSDavid van Moolenbroek assert(node != NULL);
432e4e21ee1SDavid van Moolenbroek
433*6f3e0bcdSDavid van Moolenbroek /*
434*6f3e0bcdSDavid van Moolenbroek * The root node determines the version of the entire tree. Do not use
435*6f3e0bcdSDavid van Moolenbroek * version number 0, as a zero version number indicates no interest in
436*6f3e0bcdSDavid van Moolenbroek * versions elsewhere.
437*6f3e0bcdSDavid van Moolenbroek */
438*6f3e0bcdSDavid van Moolenbroek
439*6f3e0bcdSDavid van Moolenbroek ver = mib_root.node_ver + 1;
440e4e21ee1SDavid van Moolenbroek if (ver == 0)
441e4e21ee1SDavid van Moolenbroek ver = 1;
442e4e21ee1SDavid van Moolenbroek
443e4e21ee1SDavid van Moolenbroek /* Copy the new version to all the nodes on the path. */
444*6f3e0bcdSDavid van Moolenbroek do {
445*6f3e0bcdSDavid van Moolenbroek node->node_ver = ver;
446e4e21ee1SDavid van Moolenbroek
447*6f3e0bcdSDavid van Moolenbroek node = node->node_parent;
448*6f3e0bcdSDavid van Moolenbroek } while (node != NULL);
449*6f3e0bcdSDavid van Moolenbroek }
450*6f3e0bcdSDavid van Moolenbroek
451*6f3e0bcdSDavid van Moolenbroek /*
452*6f3e0bcdSDavid van Moolenbroek * Add a new dynamically allocated node into the tree, inserting it into the
453*6f3e0bcdSDavid van Moolenbroek * linked-list position of the parent tree as given by 'prevp'. Also update
454*6f3e0bcdSDavid van Moolenbroek * versions and counters accordingly. This function never fails.
455*6f3e0bcdSDavid van Moolenbroek */
456*6f3e0bcdSDavid van Moolenbroek static void
mib_add(struct mib_dynode * dynode,struct mib_dynode ** prevp)457*6f3e0bcdSDavid van Moolenbroek mib_add(struct mib_dynode * dynode, struct mib_dynode ** prevp)
458*6f3e0bcdSDavid van Moolenbroek {
459*6f3e0bcdSDavid van Moolenbroek struct mib_node *parent;
460*6f3e0bcdSDavid van Moolenbroek
461*6f3e0bcdSDavid van Moolenbroek parent = dynode->dynode_node.node_parent;
462*6f3e0bcdSDavid van Moolenbroek assert(parent != NULL);
463*6f3e0bcdSDavid van Moolenbroek
464*6f3e0bcdSDavid van Moolenbroek /* Link the dynamic node into the list, in the right place. */
465*6f3e0bcdSDavid van Moolenbroek assert(prevp != NULL);
466*6f3e0bcdSDavid van Moolenbroek dynode->dynode_next = *prevp;
467*6f3e0bcdSDavid van Moolenbroek *prevp = dynode;
468*6f3e0bcdSDavid van Moolenbroek
469*6f3e0bcdSDavid van Moolenbroek /* The parent node now has one more child. */
470*6f3e0bcdSDavid van Moolenbroek parent->node_csize++;
471*6f3e0bcdSDavid van Moolenbroek parent->node_clen++;
472*6f3e0bcdSDavid van Moolenbroek
473*6f3e0bcdSDavid van Moolenbroek /* There is now one more node in the tree. */
474*6f3e0bcdSDavid van Moolenbroek mib_nodes++;
475*6f3e0bcdSDavid van Moolenbroek
476*6f3e0bcdSDavid van Moolenbroek /*
477*6f3e0bcdSDavid van Moolenbroek * Bump the version of all nodes on the path to the new node, including
478*6f3e0bcdSDavid van Moolenbroek * the node itself.
479*6f3e0bcdSDavid van Moolenbroek */
480*6f3e0bcdSDavid van Moolenbroek mib_upgrade(&dynode->dynode_node);
481e4e21ee1SDavid van Moolenbroek }
482e4e21ee1SDavid van Moolenbroek
483e4e21ee1SDavid van Moolenbroek /*
484e4e21ee1SDavid van Moolenbroek * Create a node.
485e4e21ee1SDavid van Moolenbroek */
486e4e21ee1SDavid van Moolenbroek static ssize_t
mib_create(struct mib_call * call,struct mib_node * parent,struct mib_oldp * oldp,struct mib_newp * newp)487e4e21ee1SDavid van Moolenbroek mib_create(struct mib_call * call, struct mib_node * parent,
488*6f3e0bcdSDavid van Moolenbroek struct mib_oldp * oldp, struct mib_newp * newp)
489e4e21ee1SDavid van Moolenbroek {
490e4e21ee1SDavid van Moolenbroek struct mib_dynode *dynode, **prevp;
491e4e21ee1SDavid van Moolenbroek struct mib_node *node;
492e4e21ee1SDavid van Moolenbroek struct sysctlnode scn;
493e4e21ee1SDavid van Moolenbroek size_t namelen, size;
494e4e21ee1SDavid van Moolenbroek ssize_t len;
495e4e21ee1SDavid van Moolenbroek bool b;
496e4e21ee1SDavid van Moolenbroek char c;
497e4e21ee1SDavid van Moolenbroek int r, id;
498e4e21ee1SDavid van Moolenbroek
499e4e21ee1SDavid van Moolenbroek /* This is a privileged operation. */
500e4e21ee1SDavid van Moolenbroek if (!mib_authed(call))
501e4e21ee1SDavid van Moolenbroek return EPERM;
502e4e21ee1SDavid van Moolenbroek
503*6f3e0bcdSDavid van Moolenbroek /*
504*6f3e0bcdSDavid van Moolenbroek * The parent must not be a remote node, but this is already implied by
505*6f3e0bcdSDavid van Moolenbroek * the fact that we got here at all.
506*6f3e0bcdSDavid van Moolenbroek */
507*6f3e0bcdSDavid van Moolenbroek assert(SYSCTL_TYPE(parent->node_flags) == CTLTYPE_NODE);
508*6f3e0bcdSDavid van Moolenbroek assert(!(parent->node_flags & CTLFLAG_REMOTE));
509*6f3e0bcdSDavid van Moolenbroek
510e4e21ee1SDavid van Moolenbroek /* The parent node must not be marked as read-only. */
511e4e21ee1SDavid van Moolenbroek if (!(parent->node_flags & CTLFLAG_READWRITE))
512e4e21ee1SDavid van Moolenbroek return EPERM;
513e4e21ee1SDavid van Moolenbroek
514e4e21ee1SDavid van Moolenbroek /*
515e4e21ee1SDavid van Moolenbroek * Has the parent reached its child node limit? This check is entirely
516e4e21ee1SDavid van Moolenbroek * theoretical as long as we support only 32-bit virtual memory.
517e4e21ee1SDavid van Moolenbroek */
518e4e21ee1SDavid van Moolenbroek if (parent->node_csize == INT_MAX)
519e4e21ee1SDavid van Moolenbroek return EINVAL;
520e4e21ee1SDavid van Moolenbroek assert(parent->node_clen <= parent->node_csize);
521e4e21ee1SDavid van Moolenbroek
522e4e21ee1SDavid van Moolenbroek /* The caller must supply information on the child node to create. */
523e4e21ee1SDavid van Moolenbroek if (newp == NULL)
524e4e21ee1SDavid van Moolenbroek return EINVAL;
525e4e21ee1SDavid van Moolenbroek
526e4e21ee1SDavid van Moolenbroek if ((r = mib_copyin(newp, &scn, sizeof(scn))) != OK)
527e4e21ee1SDavid van Moolenbroek return r;
528e4e21ee1SDavid van Moolenbroek
529e4e21ee1SDavid van Moolenbroek /*
530e4e21ee1SDavid van Moolenbroek * We perform as many checks as possible before we start allocating
531e4e21ee1SDavid van Moolenbroek * memory. Then again, after allocation, copying in data may still
532e4e21ee1SDavid van Moolenbroek * fail. Unlike when setting values, we do not first copy data into a
533e4e21ee1SDavid van Moolenbroek * temporary buffer here, because we do not need to: if the copy fails,
534e4e21ee1SDavid van Moolenbroek * the entire create operation fails, so atomicity is not an issue.
535e4e21ee1SDavid van Moolenbroek */
536e4e21ee1SDavid van Moolenbroek if (SYSCTL_VERS(scn.sysctl_flags) != SYSCTL_VERSION)
537e4e21ee1SDavid van Moolenbroek return EINVAL;
538e4e21ee1SDavid van Moolenbroek
539e4e21ee1SDavid van Moolenbroek /*
540*6f3e0bcdSDavid van Moolenbroek * If a node version number is given, it must match the version of
541*6f3e0bcdSDavid van Moolenbroek * either the parent or the root node. The given version number is
542*6f3e0bcdSDavid van Moolenbroek * *not* used for the node being created.
543e4e21ee1SDavid van Moolenbroek */
544*6f3e0bcdSDavid van Moolenbroek if (scn.sysctl_ver != 0 && scn.sysctl_ver != mib_root.node_ver &&
545e4e21ee1SDavid van Moolenbroek scn.sysctl_ver != parent->node_ver)
546e4e21ee1SDavid van Moolenbroek return EINVAL;
547e4e21ee1SDavid van Moolenbroek
548e4e21ee1SDavid van Moolenbroek /*
549e4e21ee1SDavid van Moolenbroek * Validate the node flags. In addition to the NetBSD-allowed flags,
550e4e21ee1SDavid van Moolenbroek * we also allow UNSIGNED, and leave its interpretation to userland.
551e4e21ee1SDavid van Moolenbroek */
552e4e21ee1SDavid van Moolenbroek if (SYSCTL_FLAGS(scn.sysctl_flags) &
553e4e21ee1SDavid van Moolenbroek ~(SYSCTL_USERFLAGS | CTLFLAG_UNSIGNED))
554e4e21ee1SDavid van Moolenbroek return EINVAL;
555e4e21ee1SDavid van Moolenbroek
556e4e21ee1SDavid van Moolenbroek if (!(scn.sysctl_flags & CTLFLAG_IMMEDIATE)) {
557e4e21ee1SDavid van Moolenbroek /*
558e4e21ee1SDavid van Moolenbroek * Without either IMMEDIATE or OWNDATA, data pointers are
559e4e21ee1SDavid van Moolenbroek * actually kernel addresses--a concept we do not support.
560e4e21ee1SDavid van Moolenbroek * Otherwise, if IMMEDIATE is not set, we are going to have to
561e4e21ee1SDavid van Moolenbroek * allocate extra memory for the data, so force OWNDATA to be.
562e4e21ee1SDavid van Moolenbroek * set. Node-type nodes have no data, though.
563e4e21ee1SDavid van Moolenbroek */
564e4e21ee1SDavid van Moolenbroek if (SYSCTL_TYPE(scn.sysctl_flags) != CTLTYPE_NODE) {
565e4e21ee1SDavid van Moolenbroek if (!(scn.sysctl_flags & CTLFLAG_OWNDATA) &&
566e4e21ee1SDavid van Moolenbroek scn.sysctl_data != NULL)
567e4e21ee1SDavid van Moolenbroek return EINVAL; /* not meaningful on MINIX3 */
568e4e21ee1SDavid van Moolenbroek
569e4e21ee1SDavid van Moolenbroek scn.sysctl_flags |= CTLFLAG_OWNDATA;
570e4e21ee1SDavid van Moolenbroek }
571e4e21ee1SDavid van Moolenbroek } else if (scn.sysctl_flags & CTLFLAG_OWNDATA)
572e4e21ee1SDavid van Moolenbroek return EINVAL;
573e4e21ee1SDavid van Moolenbroek
574e4e21ee1SDavid van Moolenbroek /* The READWRITE flag consists of multiple bits. Sanitize. */
575e4e21ee1SDavid van Moolenbroek if (scn.sysctl_flags & CTLFLAG_READWRITE)
576e4e21ee1SDavid van Moolenbroek scn.sysctl_flags |= CTLFLAG_READWRITE;
577e4e21ee1SDavid van Moolenbroek
578e4e21ee1SDavid van Moolenbroek /* Validate the node type and size, and do some additional checks. */
579e4e21ee1SDavid van Moolenbroek switch (SYSCTL_TYPE(scn.sysctl_flags)) {
580e4e21ee1SDavid van Moolenbroek case CTLTYPE_BOOL:
581e4e21ee1SDavid van Moolenbroek if (scn.sysctl_size != sizeof(bool))
582e4e21ee1SDavid van Moolenbroek return EINVAL;
583e4e21ee1SDavid van Moolenbroek break;
584e4e21ee1SDavid van Moolenbroek case CTLTYPE_INT:
585e4e21ee1SDavid van Moolenbroek if (scn.sysctl_size != sizeof(int))
586e4e21ee1SDavid van Moolenbroek return EINVAL;
587e4e21ee1SDavid van Moolenbroek break;
588e4e21ee1SDavid van Moolenbroek case CTLTYPE_QUAD:
589e4e21ee1SDavid van Moolenbroek if (scn.sysctl_size != sizeof(u_quad_t))
590e4e21ee1SDavid van Moolenbroek return EINVAL;
591e4e21ee1SDavid van Moolenbroek break;
592e4e21ee1SDavid van Moolenbroek case CTLTYPE_STRING:
593e4e21ee1SDavid van Moolenbroek /*
594e4e21ee1SDavid van Moolenbroek * For strings, a zero length means that we are supposed to
595e4e21ee1SDavid van Moolenbroek * allocate a buffer size based on the given string size.
596e4e21ee1SDavid van Moolenbroek */
597e4e21ee1SDavid van Moolenbroek if (scn.sysctl_size == 0 && scn.sysctl_data != NULL) {
598e4e21ee1SDavid van Moolenbroek if ((r = mib_copyin_str(newp,
599e4e21ee1SDavid van Moolenbroek (vir_bytes)scn.sysctl_data, NULL, SSIZE_MAX,
600e4e21ee1SDavid van Moolenbroek &size)) != OK)
601e4e21ee1SDavid van Moolenbroek return r;
602e4e21ee1SDavid van Moolenbroek scn.sysctl_size = size;
603e4e21ee1SDavid van Moolenbroek }
604e4e21ee1SDavid van Moolenbroek /* FALLTHROUGH */
605e4e21ee1SDavid van Moolenbroek case CTLTYPE_STRUCT:
606e4e21ee1SDavid van Moolenbroek /*
607e4e21ee1SDavid van Moolenbroek * We do not set an upper size on the data size, since it would
608e4e21ee1SDavid van Moolenbroek * still be possible to create a large number of nodes, and
609e4e21ee1SDavid van Moolenbroek * this is a privileged operation ayway.
610e4e21ee1SDavid van Moolenbroek */
611e4e21ee1SDavid van Moolenbroek if (scn.sysctl_size == 0 || scn.sysctl_size > SSIZE_MAX)
612e4e21ee1SDavid van Moolenbroek return EINVAL;
613e4e21ee1SDavid van Moolenbroek if (scn.sysctl_flags & CTLFLAG_IMMEDIATE)
614e4e21ee1SDavid van Moolenbroek return EINVAL;
615e4e21ee1SDavid van Moolenbroek break;
616e4e21ee1SDavid van Moolenbroek case CTLTYPE_NODE:
617e4e21ee1SDavid van Moolenbroek /*
618e4e21ee1SDavid van Moolenbroek * The zero size is an API requirement, but we also rely on the
619e4e21ee1SDavid van Moolenbroek * zero value internally, as the node has no static children.
620e4e21ee1SDavid van Moolenbroek */
621e4e21ee1SDavid van Moolenbroek if (scn.sysctl_size != 0)
622e4e21ee1SDavid van Moolenbroek return EINVAL;
623e4e21ee1SDavid van Moolenbroek if (scn.sysctl_flags & (CTLFLAG_IMMEDIATE | CTLFLAG_OWNDATA))
624e4e21ee1SDavid van Moolenbroek return EINVAL;
625e4e21ee1SDavid van Moolenbroek if (scn.sysctl_csize != 0 || scn.sysctl_clen != 0 ||
626e4e21ee1SDavid van Moolenbroek scn.sysctl_child != NULL)
627e4e21ee1SDavid van Moolenbroek return EINVAL;
628e4e21ee1SDavid van Moolenbroek break;
629e4e21ee1SDavid van Moolenbroek default:
630e4e21ee1SDavid van Moolenbroek return EINVAL;
631e4e21ee1SDavid van Moolenbroek }
632e4e21ee1SDavid van Moolenbroek
633e4e21ee1SDavid van Moolenbroek if (scn.sysctl_func != NULL || scn.sysctl_parent != NULL)
634e4e21ee1SDavid van Moolenbroek return EINVAL;
635e4e21ee1SDavid van Moolenbroek
636*6f3e0bcdSDavid van Moolenbroek /* Verify that the given name is valid, and get its string length. */
637*6f3e0bcdSDavid van Moolenbroek namelen = mib_check_name(scn.sysctl_name, sizeof(scn.sysctl_name));
638*6f3e0bcdSDavid van Moolenbroek
639*6f3e0bcdSDavid van Moolenbroek if (namelen == 0)
640e4e21ee1SDavid van Moolenbroek return EINVAL;
641e4e21ee1SDavid van Moolenbroek
642e4e21ee1SDavid van Moolenbroek /*
643e4e21ee1SDavid van Moolenbroek * Find a free identifier, or check for ID collisions if a specific
644e4e21ee1SDavid van Moolenbroek * identifier was given. At the same time, scan for name collisions,
645e4e21ee1SDavid van Moolenbroek * and find the location at which to insert the new node in the list.
646e4e21ee1SDavid van Moolenbroek */
647e4e21ee1SDavid van Moolenbroek r = mib_scan(parent, scn.sysctl_num, scn.sysctl_name, &id, &prevp,
648e4e21ee1SDavid van Moolenbroek &node);
649e4e21ee1SDavid van Moolenbroek
650e4e21ee1SDavid van Moolenbroek if (r != OK) {
651e4e21ee1SDavid van Moolenbroek /*
652e4e21ee1SDavid van Moolenbroek * On collisions, if requested, copy out the existing node.
653e4e21ee1SDavid van Moolenbroek * This does not quite fit the general interaction model, as
654e4e21ee1SDavid van Moolenbroek * the service must now return a nonzero old length from a call
655e4e21ee1SDavid van Moolenbroek * that actually failed (in contrast to ENOMEM failures).
656e4e21ee1SDavid van Moolenbroek */
657e4e21ee1SDavid van Moolenbroek if (r == EEXIST && oldp != NULL) {
658e4e21ee1SDavid van Moolenbroek len = mib_copyout_node(call, oldp, 0, id, node);
659e4e21ee1SDavid van Moolenbroek
660e4e21ee1SDavid van Moolenbroek if (len > 0)
661e4e21ee1SDavid van Moolenbroek mib_setoldlen(call, len);
662e4e21ee1SDavid van Moolenbroek }
663e4e21ee1SDavid van Moolenbroek
664e4e21ee1SDavid van Moolenbroek return r;
665e4e21ee1SDavid van Moolenbroek }
666e4e21ee1SDavid van Moolenbroek
667e4e21ee1SDavid van Moolenbroek /*
668e4e21ee1SDavid van Moolenbroek * All checks so far have passed. "id" now contains the new node
669e4e21ee1SDavid van Moolenbroek * identifier, and "prevp" points to the pointer at which to insert the
670e4e21ee1SDavid van Moolenbroek * new node in its parent's linked list of dynamic nodes.
671e4e21ee1SDavid van Moolenbroek *
672e4e21ee1SDavid van Moolenbroek * We can now attempt to create and initialize a new dynamic node.
673e4e21ee1SDavid van Moolenbroek * Allocating nodes this way may cause heavy memory fragmentation over
674e4e21ee1SDavid van Moolenbroek * time, but we do not expect the tree to see heavy modification at run
675e4e21ee1SDavid van Moolenbroek * time, and the superuser has easier ways to get the MIB service in
676e4e21ee1SDavid van Moolenbroek * trouble. We note that even in low-memory conditions, the MIB
677e4e21ee1SDavid van Moolenbroek * service is always able to provide basic functionality.
678e4e21ee1SDavid van Moolenbroek */
679e4e21ee1SDavid van Moolenbroek size = sizeof(*dynode) + namelen;
680e4e21ee1SDavid van Moolenbroek if (!(scn.sysctl_flags & CTLFLAG_IMMEDIATE))
681e4e21ee1SDavid van Moolenbroek size += scn.sysctl_size;
682e4e21ee1SDavid van Moolenbroek
683e4e21ee1SDavid van Moolenbroek if ((dynode = malloc(size)) == NULL)
684e4e21ee1SDavid van Moolenbroek return EINVAL; /* do not return ENOMEM */
685*6f3e0bcdSDavid van Moolenbroek mib_objects++;
686e4e21ee1SDavid van Moolenbroek
687e4e21ee1SDavid van Moolenbroek /* From here on, we have to free "dynode" before returning an error. */
688e4e21ee1SDavid van Moolenbroek r = OK;
689e4e21ee1SDavid van Moolenbroek
690e4e21ee1SDavid van Moolenbroek memset(dynode, 0, sizeof(*dynode)); /* no need to zero all of "size" */
691e4e21ee1SDavid van Moolenbroek dynode->dynode_id = id;
692e4e21ee1SDavid van Moolenbroek strlcpy(dynode->dynode_name, scn.sysctl_name, namelen + 1);
693e4e21ee1SDavid van Moolenbroek
694e4e21ee1SDavid van Moolenbroek node = &dynode->dynode_node;
695e4e21ee1SDavid van Moolenbroek node->node_flags = scn.sysctl_flags & ~SYSCTL_VERS_MASK;
696e4e21ee1SDavid van Moolenbroek if (SYSCTL_TYPE(scn.sysctl_flags) == CTLTYPE_NODE)
697e4e21ee1SDavid van Moolenbroek node->node_flags |= CTLFLAG_PARENT;
698e4e21ee1SDavid van Moolenbroek node->node_size = scn.sysctl_size;
699*6f3e0bcdSDavid van Moolenbroek node->node_parent = parent;
700e4e21ee1SDavid van Moolenbroek node->node_name = dynode->dynode_name;
701e4e21ee1SDavid van Moolenbroek
702e4e21ee1SDavid van Moolenbroek /* Initialize the node value. */
703e4e21ee1SDavid van Moolenbroek if (scn.sysctl_flags & CTLFLAG_IMMEDIATE) {
704e4e21ee1SDavid van Moolenbroek switch (SYSCTL_TYPE(scn.sysctl_flags)) {
705e4e21ee1SDavid van Moolenbroek case CTLTYPE_BOOL:
706e4e21ee1SDavid van Moolenbroek /* Sanitize booleans. See the C99 _Bool comment. */
707e4e21ee1SDavid van Moolenbroek memcpy(&c, &scn.sysctl_bdata, sizeof(c));
708e4e21ee1SDavid van Moolenbroek node->node_bool = (bool)c;
709e4e21ee1SDavid van Moolenbroek break;
710e4e21ee1SDavid van Moolenbroek case CTLTYPE_INT:
711e4e21ee1SDavid van Moolenbroek node->node_int = scn.sysctl_idata;
712e4e21ee1SDavid van Moolenbroek break;
713e4e21ee1SDavid van Moolenbroek case CTLTYPE_QUAD:
714e4e21ee1SDavid van Moolenbroek node->node_quad = scn.sysctl_qdata;
715e4e21ee1SDavid van Moolenbroek break;
716e4e21ee1SDavid van Moolenbroek default:
717e4e21ee1SDavid van Moolenbroek assert(0);
718e4e21ee1SDavid van Moolenbroek }
719e4e21ee1SDavid van Moolenbroek } else if (SYSCTL_TYPE(scn.sysctl_flags) != CTLTYPE_NODE) {
720e4e21ee1SDavid van Moolenbroek node->node_data = dynode->dynode_name + namelen + 1;
721e4e21ee1SDavid van Moolenbroek
722e4e21ee1SDavid van Moolenbroek /* Did the user supply initial data? If not, use zeroes. */
723e4e21ee1SDavid van Moolenbroek if (scn.sysctl_data != NULL) {
724e4e21ee1SDavid van Moolenbroek /*
725e4e21ee1SDavid van Moolenbroek * For strings, do not copy in more than needed. This
726e4e21ee1SDavid van Moolenbroek * is just a nice feature which allows initialization
727e4e21ee1SDavid van Moolenbroek * of large string buffers with short strings.
728e4e21ee1SDavid van Moolenbroek */
729e4e21ee1SDavid van Moolenbroek if (SYSCTL_TYPE(scn.sysctl_flags) == CTLTYPE_STRING)
730e4e21ee1SDavid van Moolenbroek r = mib_copyin_str(newp,
731e4e21ee1SDavid van Moolenbroek (vir_bytes)scn.sysctl_data,
732e4e21ee1SDavid van Moolenbroek node->node_data, scn.sysctl_size, NULL);
733e4e21ee1SDavid van Moolenbroek else
734e4e21ee1SDavid van Moolenbroek r = mib_copyin_aux(newp,
735e4e21ee1SDavid van Moolenbroek (vir_bytes)scn.sysctl_data,
736e4e21ee1SDavid van Moolenbroek node->node_data, scn.sysctl_size);
737e4e21ee1SDavid van Moolenbroek } else
738e4e21ee1SDavid van Moolenbroek memset(node->node_data, 0, scn.sysctl_size);
739e4e21ee1SDavid van Moolenbroek
740e4e21ee1SDavid van Moolenbroek /*
741e4e21ee1SDavid van Moolenbroek * Sanitize booleans. See the C99 _Bool comment elsewhere.
742e4e21ee1SDavid van Moolenbroek * In this case it is not as big of a deal, as we will not be
743e4e21ee1SDavid van Moolenbroek * accessing the boolean value directly ourselves.
744e4e21ee1SDavid van Moolenbroek */
745e4e21ee1SDavid van Moolenbroek if (r == OK && SYSCTL_TYPE(scn.sysctl_flags) == CTLTYPE_BOOL) {
746e4e21ee1SDavid van Moolenbroek b = (bool)*(char *)node->node_data;
747e4e21ee1SDavid van Moolenbroek memcpy(node->node_data, &b, sizeof(b));
748e4e21ee1SDavid van Moolenbroek }
749e4e21ee1SDavid van Moolenbroek }
750e4e21ee1SDavid van Moolenbroek
751e4e21ee1SDavid van Moolenbroek /*
752e4e21ee1SDavid van Moolenbroek * Even though it would be entirely possible to set a description right
753e4e21ee1SDavid van Moolenbroek * away as well, this does not seem to be supported on NetBSD at all.
754e4e21ee1SDavid van Moolenbroek */
755e4e21ee1SDavid van Moolenbroek
756e4e21ee1SDavid van Moolenbroek /* Deal with earlier failures now. */
757e4e21ee1SDavid van Moolenbroek if (r != OK) {
758e4e21ee1SDavid van Moolenbroek free(dynode);
759*6f3e0bcdSDavid van Moolenbroek mib_objects--;
760e4e21ee1SDavid van Moolenbroek
761e4e21ee1SDavid van Moolenbroek return r;
762e4e21ee1SDavid van Moolenbroek }
763e4e21ee1SDavid van Moolenbroek
764e4e21ee1SDavid van Moolenbroek /*
765*6f3e0bcdSDavid van Moolenbroek * At this point, actual creation can no longer fail. Add the node
766*6f3e0bcdSDavid van Moolenbroek * into the tree, and update versions and counters.
767e4e21ee1SDavid van Moolenbroek */
768*6f3e0bcdSDavid van Moolenbroek mib_add(dynode, prevp);
769e4e21ee1SDavid van Moolenbroek
770e4e21ee1SDavid van Moolenbroek /*
771e4e21ee1SDavid van Moolenbroek * Copy out the newly created node as resulting ("old") data. Do not
772e4e21ee1SDavid van Moolenbroek * undo the creation if this fails, though.
773e4e21ee1SDavid van Moolenbroek */
774e4e21ee1SDavid van Moolenbroek return mib_copyout_node(call, oldp, 0, id, node);
775e4e21ee1SDavid van Moolenbroek }
776e4e21ee1SDavid van Moolenbroek
777e4e21ee1SDavid van Moolenbroek /*
778*6f3e0bcdSDavid van Moolenbroek * Remove the given node from the tree. If 'prevp' is NULL, the node is a
779*6f3e0bcdSDavid van Moolenbroek * static node which should be zeroed out. If 'prevp' is not NULL, the node is
780*6f3e0bcdSDavid van Moolenbroek * a dynamic node which should be freed; 'prevp' will then point to the pointer
781*6f3e0bcdSDavid van Moolenbroek * to its dynode container. Also update versions and counters as appropriate.
782*6f3e0bcdSDavid van Moolenbroek * This function never fails.
783*6f3e0bcdSDavid van Moolenbroek */
784*6f3e0bcdSDavid van Moolenbroek static void
mib_remove(struct mib_node * node,struct mib_dynode ** prevp)785*6f3e0bcdSDavid van Moolenbroek mib_remove(struct mib_node * node, struct mib_dynode ** prevp)
786*6f3e0bcdSDavid van Moolenbroek {
787*6f3e0bcdSDavid van Moolenbroek struct mib_dynode *dynode;
788*6f3e0bcdSDavid van Moolenbroek struct mib_node *parent;
789*6f3e0bcdSDavid van Moolenbroek
790*6f3e0bcdSDavid van Moolenbroek parent = node->node_parent;
791*6f3e0bcdSDavid van Moolenbroek assert(parent != NULL);
792*6f3e0bcdSDavid van Moolenbroek
793*6f3e0bcdSDavid van Moolenbroek /* If the description was allocated, free it. */
794*6f3e0bcdSDavid van Moolenbroek if (node->node_flags & CTLFLAG_OWNDESC) {
795*6f3e0bcdSDavid van Moolenbroek free(__UNCONST(node->node_desc));
796*6f3e0bcdSDavid van Moolenbroek mib_objects--;
797*6f3e0bcdSDavid van Moolenbroek }
798*6f3e0bcdSDavid van Moolenbroek
799*6f3e0bcdSDavid van Moolenbroek /*
800*6f3e0bcdSDavid van Moolenbroek * Static nodes only use static memory, and dynamic nodes have the data
801*6f3e0bcdSDavid van Moolenbroek * area embedded in the dynode object. In neither case is data memory
802*6f3e0bcdSDavid van Moolenbroek * allocated separately, and thus, it need never be freed separately.
803*6f3e0bcdSDavid van Moolenbroek * Therefore we *must not* check CTLFLAG_OWNDATA here.
804*6f3e0bcdSDavid van Moolenbroek */
805*6f3e0bcdSDavid van Moolenbroek
806*6f3e0bcdSDavid van Moolenbroek assert(parent->node_csize > 0);
807*6f3e0bcdSDavid van Moolenbroek assert(parent->node_clen > 0);
808*6f3e0bcdSDavid van Moolenbroek
809*6f3e0bcdSDavid van Moolenbroek /*
810*6f3e0bcdSDavid van Moolenbroek * Dynamic nodes must be freed. Freeing the dynode object also frees
811*6f3e0bcdSDavid van Moolenbroek * the node name and any associated data. Static nodes are zeroed out,
812*6f3e0bcdSDavid van Moolenbroek * and the static memory they referenced will become inaccessible.
813*6f3e0bcdSDavid van Moolenbroek */
814*6f3e0bcdSDavid van Moolenbroek if (prevp != NULL) {
815*6f3e0bcdSDavid van Moolenbroek dynode = *prevp;
816*6f3e0bcdSDavid van Moolenbroek *prevp = dynode->dynode_next;
817*6f3e0bcdSDavid van Moolenbroek
818*6f3e0bcdSDavid van Moolenbroek assert(node == &dynode->dynode_node);
819*6f3e0bcdSDavid van Moolenbroek
820*6f3e0bcdSDavid van Moolenbroek free(dynode);
821*6f3e0bcdSDavid van Moolenbroek mib_objects--;
822*6f3e0bcdSDavid van Moolenbroek
823*6f3e0bcdSDavid van Moolenbroek parent->node_csize--;
824*6f3e0bcdSDavid van Moolenbroek } else
825*6f3e0bcdSDavid van Moolenbroek memset(node, 0, sizeof(*node));
826*6f3e0bcdSDavid van Moolenbroek
827*6f3e0bcdSDavid van Moolenbroek parent->node_clen--;
828*6f3e0bcdSDavid van Moolenbroek
829*6f3e0bcdSDavid van Moolenbroek mib_nodes--;
830*6f3e0bcdSDavid van Moolenbroek
831*6f3e0bcdSDavid van Moolenbroek /* Bump the version of all nodes on the path to the destroyed node. */
832*6f3e0bcdSDavid van Moolenbroek mib_upgrade(parent);
833*6f3e0bcdSDavid van Moolenbroek }
834*6f3e0bcdSDavid van Moolenbroek
835*6f3e0bcdSDavid van Moolenbroek /*
836e4e21ee1SDavid van Moolenbroek * Destroy a node.
837e4e21ee1SDavid van Moolenbroek */
838e4e21ee1SDavid van Moolenbroek static ssize_t
mib_destroy(struct mib_call * call,struct mib_node * parent,struct mib_oldp * oldp,struct mib_newp * newp)839e4e21ee1SDavid van Moolenbroek mib_destroy(struct mib_call * call, struct mib_node * parent,
840*6f3e0bcdSDavid van Moolenbroek struct mib_oldp * oldp, struct mib_newp * newp)
841e4e21ee1SDavid van Moolenbroek {
842*6f3e0bcdSDavid van Moolenbroek struct mib_dynode **prevp;
843e4e21ee1SDavid van Moolenbroek struct mib_node *node;
844e4e21ee1SDavid van Moolenbroek struct sysctlnode scn;
845e4e21ee1SDavid van Moolenbroek ssize_t r;
846e4e21ee1SDavid van Moolenbroek
847e4e21ee1SDavid van Moolenbroek /* This is a privileged operation. */
848e4e21ee1SDavid van Moolenbroek if (!mib_authed(call))
849e4e21ee1SDavid van Moolenbroek return EPERM;
850e4e21ee1SDavid van Moolenbroek
851e4e21ee1SDavid van Moolenbroek /* The parent node must not be marked as read-only. */
852e4e21ee1SDavid van Moolenbroek if (!(parent->node_flags & CTLFLAG_READWRITE))
853e4e21ee1SDavid van Moolenbroek return EPERM;
854e4e21ee1SDavid van Moolenbroek
855e4e21ee1SDavid van Moolenbroek /* The caller must specify which child node to destroy. */
856e4e21ee1SDavid van Moolenbroek if (newp == NULL)
857e4e21ee1SDavid van Moolenbroek return EINVAL;
858e4e21ee1SDavid van Moolenbroek
859e4e21ee1SDavid van Moolenbroek if ((r = mib_copyin(newp, &scn, sizeof(scn))) != OK)
860e4e21ee1SDavid van Moolenbroek return r;
861e4e21ee1SDavid van Moolenbroek
862e4e21ee1SDavid van Moolenbroek if (SYSCTL_VERS(scn.sysctl_flags) != SYSCTL_VERSION)
863e4e21ee1SDavid van Moolenbroek return EINVAL;
864e4e21ee1SDavid van Moolenbroek
865e4e21ee1SDavid van Moolenbroek /* Locate the child node. */
866e4e21ee1SDavid van Moolenbroek if ((node = mib_find(parent, scn.sysctl_num, &prevp)) == NULL)
867e4e21ee1SDavid van Moolenbroek return ENOENT;
868e4e21ee1SDavid van Moolenbroek
869e4e21ee1SDavid van Moolenbroek /* The node must not be marked as permanent. */
870e4e21ee1SDavid van Moolenbroek if (node->node_flags & CTLFLAG_PERMANENT)
871e4e21ee1SDavid van Moolenbroek return EPERM;
872e4e21ee1SDavid van Moolenbroek
873e4e21ee1SDavid van Moolenbroek /* For node-type nodes, extra rules apply. */
874e4e21ee1SDavid van Moolenbroek if (SYSCTL_TYPE(node->node_flags) == CTLTYPE_NODE) {
875*6f3e0bcdSDavid van Moolenbroek /* The node must not be a mount point. */
876*6f3e0bcdSDavid van Moolenbroek if (node->node_flags & CTLFLAG_REMOTE)
877*6f3e0bcdSDavid van Moolenbroek return EBUSY;
878*6f3e0bcdSDavid van Moolenbroek
879e4e21ee1SDavid van Moolenbroek /* The node must not have an associated function. */
880e4e21ee1SDavid van Moolenbroek if (!(node->node_flags & CTLFLAG_PARENT))
881e4e21ee1SDavid van Moolenbroek return EPERM;
882e4e21ee1SDavid van Moolenbroek
883e4e21ee1SDavid van Moolenbroek /* The target node must itself not have child nodes. */
884e4e21ee1SDavid van Moolenbroek if (node->node_clen != 0)
885e4e21ee1SDavid van Moolenbroek return ENOTEMPTY;
886e4e21ee1SDavid van Moolenbroek }
887e4e21ee1SDavid van Moolenbroek
888e4e21ee1SDavid van Moolenbroek /* If the user supplied a version, it must match the node version. */
889e4e21ee1SDavid van Moolenbroek if (scn.sysctl_ver != 0 && scn.sysctl_ver != node->node_ver)
890e4e21ee1SDavid van Moolenbroek return EINVAL; /* NetBSD inconsistently throws ENOENT here */
891e4e21ee1SDavid van Moolenbroek
892e4e21ee1SDavid van Moolenbroek /* If the user supplied a name, it must match the node name. */
893e4e21ee1SDavid van Moolenbroek if (scn.sysctl_name[0] != '\0') {
894e4e21ee1SDavid van Moolenbroek if (memchr(scn.sysctl_name, '\0',
895e4e21ee1SDavid van Moolenbroek sizeof(scn.sysctl_name)) == NULL)
896e4e21ee1SDavid van Moolenbroek return EINVAL;
897e4e21ee1SDavid van Moolenbroek if (strcmp(scn.sysctl_name, node->node_name))
898e4e21ee1SDavid van Moolenbroek return EINVAL; /* also ENOENT on NetBSD */
899e4e21ee1SDavid van Moolenbroek }
900e4e21ee1SDavid van Moolenbroek
901e4e21ee1SDavid van Moolenbroek /*
902e4e21ee1SDavid van Moolenbroek * Copy out the old node if requested, otherwise return the length
903e4e21ee1SDavid van Moolenbroek * anyway. The node will be destroyed even if this call fails,
904e4e21ee1SDavid van Moolenbroek * because that is how NetBSD behaves.
905e4e21ee1SDavid van Moolenbroek */
906e4e21ee1SDavid van Moolenbroek r = mib_copyout_node(call, oldp, 0, scn.sysctl_num, node);
907e4e21ee1SDavid van Moolenbroek
908e4e21ee1SDavid van Moolenbroek /*
909*6f3e0bcdSDavid van Moolenbroek * Remove the node from the tree. The procedure depends on whether the
910*6f3e0bcdSDavid van Moolenbroek * node is static (prevp == NULL) or dynamic (prevp != NULL). Also
911*6f3e0bcdSDavid van Moolenbroek * update versions and counters.
912e4e21ee1SDavid van Moolenbroek */
913*6f3e0bcdSDavid van Moolenbroek mib_remove(node, prevp);
914e4e21ee1SDavid van Moolenbroek
915e4e21ee1SDavid van Moolenbroek return r;
916e4e21ee1SDavid van Moolenbroek }
917e4e21ee1SDavid van Moolenbroek
918e4e21ee1SDavid van Moolenbroek /*
919e4e21ee1SDavid van Moolenbroek * Copy out a node description to userland, using the exchange format for node
920e4e21ee1SDavid van Moolenbroek * descriptions (namely, a sysctldesc structure). Return the size of the
921e4e21ee1SDavid van Moolenbroek * object that is (or, if the description falls outside the requested data
922e4e21ee1SDavid van Moolenbroek * range, would be) copied out on success, or a negative error code on failure.
923e4e21ee1SDavid van Moolenbroek * The function may return 0 to indicate that nothing was copied out after all.
924e4e21ee1SDavid van Moolenbroek */
925e4e21ee1SDavid van Moolenbroek static ssize_t
mib_copyout_desc(struct mib_call * call,struct mib_oldp * oldp,size_t off,int id,const struct mib_node * node)926e4e21ee1SDavid van Moolenbroek mib_copyout_desc(struct mib_call * call, struct mib_oldp * oldp, size_t off,
927e4e21ee1SDavid van Moolenbroek int id, const struct mib_node * node)
928e4e21ee1SDavid van Moolenbroek {
929e4e21ee1SDavid van Moolenbroek struct sysctldesc *scd;
930e4e21ee1SDavid van Moolenbroek size_t size;
931e4e21ee1SDavid van Moolenbroek int r;
932e4e21ee1SDavid van Moolenbroek
933e4e21ee1SDavid van Moolenbroek /* Descriptions of private nodes are considered private too. */
934e4e21ee1SDavid van Moolenbroek if ((node->node_flags & CTLFLAG_PRIVATE) && !mib_authed(call))
935e4e21ee1SDavid van Moolenbroek return 0;
936e4e21ee1SDavid van Moolenbroek
937e4e21ee1SDavid van Moolenbroek /* The description length includes the null terminator. */
938e4e21ee1SDavid van Moolenbroek if (node->node_desc != NULL)
939e4e21ee1SDavid van Moolenbroek size = strlen(node->node_desc) + 1;
940e4e21ee1SDavid van Moolenbroek else
941e4e21ee1SDavid van Moolenbroek size = 1;
942e4e21ee1SDavid van Moolenbroek
943e4e21ee1SDavid van Moolenbroek assert(sizeof(*scd) + size <= sizeof(scratch));
944e4e21ee1SDavid van Moolenbroek
945e4e21ee1SDavid van Moolenbroek scd = (struct sysctldesc *)scratch;
946e4e21ee1SDavid van Moolenbroek memset(scd, 0, sizeof(*scd));
947e4e21ee1SDavid van Moolenbroek scd->descr_num = id;
948e4e21ee1SDavid van Moolenbroek scd->descr_ver = node->node_ver;
949e4e21ee1SDavid van Moolenbroek scd->descr_len = size;
950e4e21ee1SDavid van Moolenbroek if (node->node_desc != NULL)
951e4e21ee1SDavid van Moolenbroek strlcpy(scd->descr_str, node->node_desc,
952e4e21ee1SDavid van Moolenbroek sizeof(scratch) - sizeof(*scd));
953e4e21ee1SDavid van Moolenbroek else
954e4e21ee1SDavid van Moolenbroek scd->descr_str[0] = '\0';
955e4e21ee1SDavid van Moolenbroek
956e4e21ee1SDavid van Moolenbroek size += offsetof(struct sysctldesc, descr_str);
957e4e21ee1SDavid van Moolenbroek
958e4e21ee1SDavid van Moolenbroek if ((r = mib_copyout(oldp, off, scratch, size)) < 0)
959e4e21ee1SDavid van Moolenbroek return r;
960e4e21ee1SDavid van Moolenbroek
961e4e21ee1SDavid van Moolenbroek /*
962e4e21ee1SDavid van Moolenbroek * By aligning just the size, we may leave garbage between the entries
963e4e21ee1SDavid van Moolenbroek * copied out, which is fine because it is userland's own data.
964e4e21ee1SDavid van Moolenbroek */
965e4e21ee1SDavid van Moolenbroek return roundup2(size, sizeof(int32_t));
966e4e21ee1SDavid van Moolenbroek }
967e4e21ee1SDavid van Moolenbroek
968e4e21ee1SDavid van Moolenbroek /*
969e4e21ee1SDavid van Moolenbroek * Retrieve node descriptions in bulk, or retrieve or assign a particular
970e4e21ee1SDavid van Moolenbroek * node's description.
971e4e21ee1SDavid van Moolenbroek */
972e4e21ee1SDavid van Moolenbroek static ssize_t
mib_describe(struct mib_call * call,struct mib_node * parent,struct mib_oldp * oldp,struct mib_newp * newp)973e4e21ee1SDavid van Moolenbroek mib_describe(struct mib_call * call, struct mib_node * parent,
974e4e21ee1SDavid van Moolenbroek struct mib_oldp * oldp, struct mib_newp * newp)
975e4e21ee1SDavid van Moolenbroek {
976e4e21ee1SDavid van Moolenbroek struct sysctlnode scn;
977e4e21ee1SDavid van Moolenbroek struct mib_node *node;
978e4e21ee1SDavid van Moolenbroek struct mib_dynode *dynode;
979e4e21ee1SDavid van Moolenbroek size_t off;
980e4e21ee1SDavid van Moolenbroek int r, id;
981e4e21ee1SDavid van Moolenbroek
982e4e21ee1SDavid van Moolenbroek /* If new data are given, they identify a particular target node. */
983e4e21ee1SDavid van Moolenbroek if (newp != NULL) {
984e4e21ee1SDavid van Moolenbroek if ((r = mib_copyin(newp, &scn, sizeof(scn))) != OK)
985e4e21ee1SDavid van Moolenbroek return r;
986e4e21ee1SDavid van Moolenbroek
987e4e21ee1SDavid van Moolenbroek if (SYSCTL_VERS(scn.sysctl_flags) != SYSCTL_VERSION)
988e4e21ee1SDavid van Moolenbroek return EINVAL;
989e4e21ee1SDavid van Moolenbroek
990e4e21ee1SDavid van Moolenbroek /* Locate the child node. */
991e4e21ee1SDavid van Moolenbroek if ((node = mib_find(parent, scn.sysctl_num, NULL)) == NULL)
992e4e21ee1SDavid van Moolenbroek return ENOENT;
993e4e21ee1SDavid van Moolenbroek
994e4e21ee1SDavid van Moolenbroek /* Descriptions of private nodes are considered private too. */
995e4e21ee1SDavid van Moolenbroek if ((node->node_flags & CTLFLAG_PRIVATE) && !mib_authed(call))
996e4e21ee1SDavid van Moolenbroek return EPERM;
997e4e21ee1SDavid van Moolenbroek
998e4e21ee1SDavid van Moolenbroek /*
999e4e21ee1SDavid van Moolenbroek * If a description pointer was given, this is a request to
1000e4e21ee1SDavid van Moolenbroek * set the node's description.
1001e4e21ee1SDavid van Moolenbroek */
1002e4e21ee1SDavid van Moolenbroek if (scn.sysctl_desc != NULL) {
1003e4e21ee1SDavid van Moolenbroek /* Such a request requires superuser privileges. */
1004e4e21ee1SDavid van Moolenbroek if (!mib_authed(call))
1005e4e21ee1SDavid van Moolenbroek return EPERM;
1006e4e21ee1SDavid van Moolenbroek
1007*6f3e0bcdSDavid van Moolenbroek /*
1008*6f3e0bcdSDavid van Moolenbroek * The node must not be a mount point. Arguably this
1009*6f3e0bcdSDavid van Moolenbroek * check is not necessary, since we use the description
1010*6f3e0bcdSDavid van Moolenbroek * of the preexisting underlying node anyway.
1011*6f3e0bcdSDavid van Moolenbroek */
1012*6f3e0bcdSDavid van Moolenbroek if (SYSCTL_TYPE(node->node_flags) == CTLTYPE_NODE &&
1013*6f3e0bcdSDavid van Moolenbroek (node->node_flags & CTLFLAG_REMOTE))
1014*6f3e0bcdSDavid van Moolenbroek return EBUSY;
1015*6f3e0bcdSDavid van Moolenbroek
1016e4e21ee1SDavid van Moolenbroek /* The node must not already have a description. */
1017e4e21ee1SDavid van Moolenbroek if (node->node_desc != NULL)
1018e4e21ee1SDavid van Moolenbroek return EPERM;
1019e4e21ee1SDavid van Moolenbroek
1020e4e21ee1SDavid van Moolenbroek /* The node must not be marked as permanent. */
1021e4e21ee1SDavid van Moolenbroek if (node->node_flags & CTLFLAG_PERMANENT)
1022e4e21ee1SDavid van Moolenbroek return EPERM;
1023e4e21ee1SDavid van Moolenbroek
1024e4e21ee1SDavid van Moolenbroek /*
1025e4e21ee1SDavid van Moolenbroek * If the user supplied a version, it must match.
1026e4e21ee1SDavid van Moolenbroek * NetBSD performs this check only when setting
1027e4e21ee1SDavid van Moolenbroek * descriptions, and thus, so do we..
1028e4e21ee1SDavid van Moolenbroek */
1029e4e21ee1SDavid van Moolenbroek if (scn.sysctl_ver != 0 &&
1030e4e21ee1SDavid van Moolenbroek scn.sysctl_ver != node->node_ver)
1031e4e21ee1SDavid van Moolenbroek return EINVAL;
1032e4e21ee1SDavid van Moolenbroek
1033e4e21ee1SDavid van Moolenbroek /*
1034e4e21ee1SDavid van Moolenbroek * Copy in the description to the scratch buffer.
1035e4e21ee1SDavid van Moolenbroek * The length of the description must be reasonable.
1036e4e21ee1SDavid van Moolenbroek */
1037e4e21ee1SDavid van Moolenbroek if ((r = mib_copyin_str(newp,
1038e4e21ee1SDavid van Moolenbroek (vir_bytes)scn.sysctl_desc, scratch, MAXDESCLEN,
1039e4e21ee1SDavid van Moolenbroek NULL)) != OK)
1040e4e21ee1SDavid van Moolenbroek return r;
1041e4e21ee1SDavid van Moolenbroek
1042e4e21ee1SDavid van Moolenbroek /* Allocate memory and store the description. */
1043e4e21ee1SDavid van Moolenbroek if ((node->node_desc = strdup(scratch)) == NULL) {
1044e4e21ee1SDavid van Moolenbroek printf("MIB: out of memory!\n");
1045e4e21ee1SDavid van Moolenbroek
1046e4e21ee1SDavid van Moolenbroek return EINVAL; /* do not return ENOMEM */
1047e4e21ee1SDavid van Moolenbroek }
1048*6f3e0bcdSDavid van Moolenbroek mib_objects++;
1049e4e21ee1SDavid van Moolenbroek
1050e4e21ee1SDavid van Moolenbroek /* The description must now be freed with the node. */
1051e4e21ee1SDavid van Moolenbroek node->node_flags |= CTLFLAG_OWNDESC;
1052e4e21ee1SDavid van Moolenbroek }
1053e4e21ee1SDavid van Moolenbroek
1054e4e21ee1SDavid van Moolenbroek /*
1055e4e21ee1SDavid van Moolenbroek * Either way, copy out the requested node's description, which
1056e4e21ee1SDavid van Moolenbroek * should indeed be the new description if one was just set.
1057e4e21ee1SDavid van Moolenbroek * Note that we have already performed the permission check
1058e4e21ee1SDavid van Moolenbroek * that could make this call return zero, so here it will not.
1059e4e21ee1SDavid van Moolenbroek */
1060e4e21ee1SDavid van Moolenbroek return mib_copyout_desc(call, oldp, 0, scn.sysctl_num, node);
1061e4e21ee1SDavid van Moolenbroek }
1062e4e21ee1SDavid van Moolenbroek
1063e4e21ee1SDavid van Moolenbroek /* See also the considerations laid out in mib_query(). */
1064e4e21ee1SDavid van Moolenbroek off = 0;
1065e4e21ee1SDavid van Moolenbroek
1066e4e21ee1SDavid van Moolenbroek /* First describe the static nodes. */
1067e4e21ee1SDavid van Moolenbroek for (id = 0; IS_STATIC_ID(parent, id); id++) {
1068e4e21ee1SDavid van Moolenbroek node = &parent->node_scptr[id];
1069e4e21ee1SDavid van Moolenbroek
1070e4e21ee1SDavid van Moolenbroek if (node->node_flags == 0)
1071e4e21ee1SDavid van Moolenbroek continue;
1072e4e21ee1SDavid van Moolenbroek
1073e4e21ee1SDavid van Moolenbroek if ((r = mib_copyout_desc(call, oldp, off, id, node)) < 0)
1074e4e21ee1SDavid van Moolenbroek return r;
1075e4e21ee1SDavid van Moolenbroek off += r;
1076e4e21ee1SDavid van Moolenbroek }
1077e4e21ee1SDavid van Moolenbroek
1078e4e21ee1SDavid van Moolenbroek /* Then describe the dynamic nodes. */
1079e4e21ee1SDavid van Moolenbroek for (dynode = parent->node_dcptr; dynode != NULL;
1080e4e21ee1SDavid van Moolenbroek dynode = dynode->dynode_next) {
1081e4e21ee1SDavid van Moolenbroek node = &dynode->dynode_node;
1082e4e21ee1SDavid van Moolenbroek
1083e4e21ee1SDavid van Moolenbroek if ((r = mib_copyout_desc(call, oldp, off, dynode->dynode_id,
1084e4e21ee1SDavid van Moolenbroek node)) < 0)
1085e4e21ee1SDavid van Moolenbroek return r;
1086e4e21ee1SDavid van Moolenbroek off += r;
1087e4e21ee1SDavid van Moolenbroek }
1088e4e21ee1SDavid van Moolenbroek
1089e4e21ee1SDavid van Moolenbroek return off;
1090e4e21ee1SDavid van Moolenbroek }
1091e4e21ee1SDavid van Moolenbroek
1092e4e21ee1SDavid van Moolenbroek /*
1093e4e21ee1SDavid van Moolenbroek * Return a pointer to the data associated with the given node, or NULL if the
1094e4e21ee1SDavid van Moolenbroek * node has no associated data. Actual calls to this function should never
1095e4e21ee1SDavid van Moolenbroek * result in NULL - as long as the proper rules are followed elsewhere.
1096e4e21ee1SDavid van Moolenbroek */
1097e4e21ee1SDavid van Moolenbroek static void *
mib_getptr(struct mib_node * node)1098e4e21ee1SDavid van Moolenbroek mib_getptr(struct mib_node * node)
1099e4e21ee1SDavid van Moolenbroek {
1100e4e21ee1SDavid van Moolenbroek
1101e4e21ee1SDavid van Moolenbroek switch (SYSCTL_TYPE(node->node_flags)) {
1102e4e21ee1SDavid van Moolenbroek case CTLTYPE_BOOL:
1103e4e21ee1SDavid van Moolenbroek if (node->node_flags & CTLFLAG_IMMEDIATE)
1104e4e21ee1SDavid van Moolenbroek return &node->node_bool;
1105e4e21ee1SDavid van Moolenbroek break;
1106e4e21ee1SDavid van Moolenbroek case CTLTYPE_INT:
1107e4e21ee1SDavid van Moolenbroek if (node->node_flags & CTLFLAG_IMMEDIATE)
1108e4e21ee1SDavid van Moolenbroek return &node->node_int;
1109e4e21ee1SDavid van Moolenbroek break;
1110e4e21ee1SDavid van Moolenbroek case CTLTYPE_QUAD:
1111e4e21ee1SDavid van Moolenbroek if (node->node_flags & CTLFLAG_IMMEDIATE)
1112e4e21ee1SDavid van Moolenbroek return &node->node_quad;
1113e4e21ee1SDavid van Moolenbroek break;
1114e4e21ee1SDavid van Moolenbroek case CTLTYPE_STRING:
1115e4e21ee1SDavid van Moolenbroek case CTLTYPE_STRUCT:
1116e4e21ee1SDavid van Moolenbroek if (node->node_flags & CTLFLAG_IMMEDIATE)
1117e4e21ee1SDavid van Moolenbroek return NULL;
1118e4e21ee1SDavid van Moolenbroek break;
1119e4e21ee1SDavid van Moolenbroek default:
1120e4e21ee1SDavid van Moolenbroek return NULL;
1121e4e21ee1SDavid van Moolenbroek }
1122e4e21ee1SDavid van Moolenbroek
1123e4e21ee1SDavid van Moolenbroek return node->node_data;
1124e4e21ee1SDavid van Moolenbroek }
1125e4e21ee1SDavid van Moolenbroek
1126e4e21ee1SDavid van Moolenbroek /*
1127e4e21ee1SDavid van Moolenbroek * Read current (old) data from a regular data node, if requested. Return the
1128e4e21ee1SDavid van Moolenbroek * old data length.
1129e4e21ee1SDavid van Moolenbroek */
1130e4e21ee1SDavid van Moolenbroek static ssize_t
mib_read(struct mib_node * node,struct mib_oldp * oldp)1131e4e21ee1SDavid van Moolenbroek mib_read(struct mib_node * node, struct mib_oldp * oldp)
1132e4e21ee1SDavid van Moolenbroek {
1133e4e21ee1SDavid van Moolenbroek void *ptr;
1134e4e21ee1SDavid van Moolenbroek size_t oldlen;
1135e4e21ee1SDavid van Moolenbroek int r;
1136e4e21ee1SDavid van Moolenbroek
1137e4e21ee1SDavid van Moolenbroek if ((ptr = mib_getptr(node)) == NULL)
1138e4e21ee1SDavid van Moolenbroek return EINVAL;
1139e4e21ee1SDavid van Moolenbroek
1140e4e21ee1SDavid van Moolenbroek if (SYSCTL_TYPE(node->node_flags) == CTLTYPE_STRING)
1141e4e21ee1SDavid van Moolenbroek oldlen = strlen(node->node_data) + 1;
1142e4e21ee1SDavid van Moolenbroek else
1143e4e21ee1SDavid van Moolenbroek oldlen = node->node_size;
1144e4e21ee1SDavid van Moolenbroek
1145e4e21ee1SDavid van Moolenbroek if (oldlen > SSIZE_MAX)
1146e4e21ee1SDavid van Moolenbroek return EINVAL;
1147e4e21ee1SDavid van Moolenbroek
1148e4e21ee1SDavid van Moolenbroek /* Copy out the current data, if requested at all. */
1149e4e21ee1SDavid van Moolenbroek if (oldp != NULL && (r = mib_copyout(oldp, 0, ptr, oldlen)) < 0)
1150e4e21ee1SDavid van Moolenbroek return r;
1151e4e21ee1SDavid van Moolenbroek
1152e4e21ee1SDavid van Moolenbroek /* Return the current length in any case. */
1153e4e21ee1SDavid van Moolenbroek return (ssize_t)oldlen;
1154e4e21ee1SDavid van Moolenbroek }
1155e4e21ee1SDavid van Moolenbroek
1156e4e21ee1SDavid van Moolenbroek /*
1157e4e21ee1SDavid van Moolenbroek * Write new data into a regular data node, if requested.
1158e4e21ee1SDavid van Moolenbroek */
1159e4e21ee1SDavid van Moolenbroek static int
mib_write(struct mib_call * call,struct mib_node * node,struct mib_newp * newp,mib_verify_ptr verify)1160e4e21ee1SDavid van Moolenbroek mib_write(struct mib_call * call, struct mib_node * node,
1161e4e21ee1SDavid van Moolenbroek struct mib_newp * newp, mib_verify_ptr verify)
1162e4e21ee1SDavid van Moolenbroek {
1163e4e21ee1SDavid van Moolenbroek bool b[(sizeof(bool) == sizeof(char)) ? 1 : -1]; /* explained below */
1164e4e21ee1SDavid van Moolenbroek char *src, *dst;
1165e4e21ee1SDavid van Moolenbroek size_t newlen;
1166e4e21ee1SDavid van Moolenbroek int r;
1167e4e21ee1SDavid van Moolenbroek
1168e4e21ee1SDavid van Moolenbroek if (newp == NULL)
1169e4e21ee1SDavid van Moolenbroek return OK; /* nothing to do */
1170e4e21ee1SDavid van Moolenbroek
1171e4e21ee1SDavid van Moolenbroek /*
1172e4e21ee1SDavid van Moolenbroek * When setting a new value, we cannot risk doing an in-place update:
1173e4e21ee1SDavid van Moolenbroek * the copy from userland may fail halfway through, in which case an
1174e4e21ee1SDavid van Moolenbroek * in-place update could leave the node value in a corrupted state.
1175e4e21ee1SDavid van Moolenbroek * Thus, we must first fetch any new data into a temporary buffer.
1176e4e21ee1SDavid van Moolenbroek *
1177e4e21ee1SDavid van Moolenbroek * Given that we use intermediate data storage, we could support value
1178e4e21ee1SDavid van Moolenbroek * swapping, where the user provides the same buffer for new and old
1179e4e21ee1SDavid van Moolenbroek * data. We choose not to: NetBSD does not support it, it would make
1180e4e21ee1SDavid van Moolenbroek * trace(1)'s job a lot harder, and it would convolute the code here.
1181e4e21ee1SDavid van Moolenbroek */
1182e4e21ee1SDavid van Moolenbroek newlen = mib_getnewlen(newp);
1183e4e21ee1SDavid van Moolenbroek
1184e4e21ee1SDavid van Moolenbroek if ((dst = mib_getptr(node)) == NULL)
1185e4e21ee1SDavid van Moolenbroek return EINVAL;
1186e4e21ee1SDavid van Moolenbroek
1187e4e21ee1SDavid van Moolenbroek switch (SYSCTL_TYPE(node->node_flags)) {
1188*6f3e0bcdSDavid van Moolenbroek case CTLTYPE_BOOL:
1189*6f3e0bcdSDavid van Moolenbroek case CTLTYPE_INT:
1190*6f3e0bcdSDavid van Moolenbroek case CTLTYPE_QUAD:
1191*6f3e0bcdSDavid van Moolenbroek case CTLTYPE_STRUCT:
1192*6f3e0bcdSDavid van Moolenbroek /* Non-string types must have an exact size match. */
1193*6f3e0bcdSDavid van Moolenbroek if (newlen != node->node_size)
1194*6f3e0bcdSDavid van Moolenbroek return EINVAL;
1195*6f3e0bcdSDavid van Moolenbroek break;
1196e4e21ee1SDavid van Moolenbroek case CTLTYPE_STRING:
1197e4e21ee1SDavid van Moolenbroek /*
1198e4e21ee1SDavid van Moolenbroek * Strings must not exceed their buffer size. There is a
1199e4e21ee1SDavid van Moolenbroek * second check further below, because we allow userland to
1200e4e21ee1SDavid van Moolenbroek * give us an unterminated string. In that case we terminate
1201e4e21ee1SDavid van Moolenbroek * it ourselves, but then the null terminator must fit as well.
1202e4e21ee1SDavid van Moolenbroek */
1203e4e21ee1SDavid van Moolenbroek if (newlen > node->node_size)
1204e4e21ee1SDavid van Moolenbroek return EINVAL;
1205e4e21ee1SDavid van Moolenbroek break;
1206e4e21ee1SDavid van Moolenbroek default:
1207e4e21ee1SDavid van Moolenbroek return EINVAL;
1208e4e21ee1SDavid van Moolenbroek }
1209e4e21ee1SDavid van Moolenbroek
1210e4e21ee1SDavid van Moolenbroek /*
1211*6f3e0bcdSDavid van Moolenbroek * If we cannot fit the data in the scratch buffer, then allocate a
1212e4e21ee1SDavid van Moolenbroek * temporary buffer. We add one extra byte so that we can add a null
1213e4e21ee1SDavid van Moolenbroek * terminator at the end of strings in case userland did not supply
1214e4e21ee1SDavid van Moolenbroek * one. Either way, we must free the temporary buffer later!
1215e4e21ee1SDavid van Moolenbroek *
1216e4e21ee1SDavid van Moolenbroek * The alternative is to ensure that the given memory is accessible
1217e4e21ee1SDavid van Moolenbroek * before starting the copy, but that would break if we ever add kernel
1218e4e21ee1SDavid van Moolenbroek * threads or anything that allows asynchronous memory unmapping, etc.
1219e4e21ee1SDavid van Moolenbroek */
1220e4e21ee1SDavid van Moolenbroek if (newlen + 1 > sizeof(scratch)) {
1221e4e21ee1SDavid van Moolenbroek /*
1222e4e21ee1SDavid van Moolenbroek * In practice, the temporary buffer is at least an entire
1223e4e21ee1SDavid van Moolenbroek * memory page, which is reasonable by any standard. As a
1224e4e21ee1SDavid van Moolenbroek * result, we can get away with refusing to perform dynamic
1225e4e21ee1SDavid van Moolenbroek * allocation for unprivileged users. This limits the impact
1226e4e21ee1SDavid van Moolenbroek * that unprivileged users can have on our memory space.
1227e4e21ee1SDavid van Moolenbroek */
1228e4e21ee1SDavid van Moolenbroek if (!mib_authed(call))
1229e4e21ee1SDavid van Moolenbroek return EPERM;
1230e4e21ee1SDavid van Moolenbroek
1231e4e21ee1SDavid van Moolenbroek /*
1232e4e21ee1SDavid van Moolenbroek * Do not return ENOMEM on allocation failure, because ENOMEM
1233e4e21ee1SDavid van Moolenbroek * implies that a valid old length was returned.
1234e4e21ee1SDavid van Moolenbroek */
1235e4e21ee1SDavid van Moolenbroek if ((src = malloc(newlen + 1)) == NULL) {
1236e4e21ee1SDavid van Moolenbroek printf("MIB: out of memory!\n");
1237e4e21ee1SDavid van Moolenbroek
1238e4e21ee1SDavid van Moolenbroek return EINVAL;
1239e4e21ee1SDavid van Moolenbroek }
1240*6f3e0bcdSDavid van Moolenbroek mib_objects++;
1241e4e21ee1SDavid van Moolenbroek } else
1242e4e21ee1SDavid van Moolenbroek src = scratch;
1243e4e21ee1SDavid van Moolenbroek
1244e4e21ee1SDavid van Moolenbroek /* Copy in the data. Note that newlen may be zero. */
1245e4e21ee1SDavid van Moolenbroek r = mib_copyin(newp, src, newlen);
1246e4e21ee1SDavid van Moolenbroek
1247e4e21ee1SDavid van Moolenbroek if (r == OK && verify != NULL && !verify(call, node, src, newlen))
1248e4e21ee1SDavid van Moolenbroek r = EINVAL;
1249e4e21ee1SDavid van Moolenbroek
1250e4e21ee1SDavid van Moolenbroek if (r == OK) {
1251e4e21ee1SDavid van Moolenbroek /* Check and, if acceptable, store the new value. */
1252e4e21ee1SDavid van Moolenbroek switch (SYSCTL_TYPE(node->node_flags)) {
1253e4e21ee1SDavid van Moolenbroek case CTLTYPE_BOOL:
1254e4e21ee1SDavid van Moolenbroek /*
1255e4e21ee1SDavid van Moolenbroek * Due to the nature of the C99 _Bool type, we can not
1256e4e21ee1SDavid van Moolenbroek * test directly whether the given boolean value is a
1257e4e21ee1SDavid van Moolenbroek * value that is not "true" and not "false". In the
1258e4e21ee1SDavid van Moolenbroek * worst case, another value could invoke undefined
1259e4e21ee1SDavid van Moolenbroek * behavior. We try our best to sanitize the value
1260e4e21ee1SDavid van Moolenbroek * without looking at it directly, which unfortunately
1261e4e21ee1SDavid van Moolenbroek * requires us to test for the size of the bool type.
1262e4e21ee1SDavid van Moolenbroek * We do that at compile time, hence the 'b' "array".
1263e4e21ee1SDavid van Moolenbroek * Any size other than one byte is an ABI violation.
1264e4e21ee1SDavid van Moolenbroek */
1265e4e21ee1SDavid van Moolenbroek b[0] = (bool)src[0];
1266e4e21ee1SDavid van Moolenbroek memcpy(dst, &b[0], sizeof(b[0]));
1267e4e21ee1SDavid van Moolenbroek break;
1268e4e21ee1SDavid van Moolenbroek case CTLTYPE_INT:
1269e4e21ee1SDavid van Moolenbroek case CTLTYPE_QUAD:
1270e4e21ee1SDavid van Moolenbroek case CTLTYPE_STRUCT:
1271e4e21ee1SDavid van Moolenbroek memcpy(dst, src, node->node_size);
1272e4e21ee1SDavid van Moolenbroek break;
1273e4e21ee1SDavid van Moolenbroek case CTLTYPE_STRING:
1274e4e21ee1SDavid van Moolenbroek if (newlen == node->node_size &&
1275e4e21ee1SDavid van Moolenbroek src[newlen - 1] != '\0') {
1276e4e21ee1SDavid van Moolenbroek /* Our null terminator does not fit! */
1277e4e21ee1SDavid van Moolenbroek r = EINVAL;
1278e4e21ee1SDavid van Moolenbroek break;
1279e4e21ee1SDavid van Moolenbroek }
1280e4e21ee1SDavid van Moolenbroek /*
1281e4e21ee1SDavid van Moolenbroek * We do not mind null characters in the middle. In
1282e4e21ee1SDavid van Moolenbroek * general, the buffer may contain garbage after the
1283e4e21ee1SDavid van Moolenbroek * first null terminator, but such garbage will never
1284e4e21ee1SDavid van Moolenbroek * end up being copied out.
1285e4e21ee1SDavid van Moolenbroek */
1286e4e21ee1SDavid van Moolenbroek src[newlen] = '\0';
1287e4e21ee1SDavid van Moolenbroek strlcpy(dst, src, node->node_size);
1288e4e21ee1SDavid van Moolenbroek break;
1289e4e21ee1SDavid van Moolenbroek default:
1290e4e21ee1SDavid van Moolenbroek r = EINVAL;
1291e4e21ee1SDavid van Moolenbroek }
1292e4e21ee1SDavid van Moolenbroek }
1293e4e21ee1SDavid van Moolenbroek
1294e4e21ee1SDavid van Moolenbroek if (src != scratch) {
1295e4e21ee1SDavid van Moolenbroek free(src);
1296*6f3e0bcdSDavid van Moolenbroek mib_objects--;
1297e4e21ee1SDavid van Moolenbroek }
1298e4e21ee1SDavid van Moolenbroek
1299e4e21ee1SDavid van Moolenbroek return r;
1300e4e21ee1SDavid van Moolenbroek }
1301e4e21ee1SDavid van Moolenbroek
1302e4e21ee1SDavid van Moolenbroek /*
1303e4e21ee1SDavid van Moolenbroek * Read and/or write the value of a regular data node. A regular data node is
1304e4e21ee1SDavid van Moolenbroek * a leaf node. Typically, a leaf node has no associated function, in which
1305e4e21ee1SDavid van Moolenbroek * case this function will be used instead. In addition, this function may be
1306e4e21ee1SDavid van Moolenbroek * used from handler functions as part of their functionality.
1307e4e21ee1SDavid van Moolenbroek */
1308e4e21ee1SDavid van Moolenbroek ssize_t
mib_readwrite(struct mib_call * call,struct mib_node * node,struct mib_oldp * oldp,struct mib_newp * newp,mib_verify_ptr verify)1309e4e21ee1SDavid van Moolenbroek mib_readwrite(struct mib_call * call, struct mib_node * node,
1310e4e21ee1SDavid van Moolenbroek struct mib_oldp * oldp, struct mib_newp * newp, mib_verify_ptr verify)
1311e4e21ee1SDavid van Moolenbroek {
1312e4e21ee1SDavid van Moolenbroek ssize_t len;
1313e4e21ee1SDavid van Moolenbroek int r;
1314e4e21ee1SDavid van Moolenbroek
1315e4e21ee1SDavid van Moolenbroek /* Copy out old data, if requested. Always get the old data length. */
1316e4e21ee1SDavid van Moolenbroek if ((r = len = mib_read(node, oldp)) < 0)
1317e4e21ee1SDavid van Moolenbroek return r;
1318e4e21ee1SDavid van Moolenbroek
1319e4e21ee1SDavid van Moolenbroek /* Copy in new data, if requested. */
1320e4e21ee1SDavid van Moolenbroek if ((r = mib_write(call, node, newp, verify)) != OK)
1321e4e21ee1SDavid van Moolenbroek return r;
1322e4e21ee1SDavid van Moolenbroek
1323e4e21ee1SDavid van Moolenbroek /* Return the old data length. */
1324e4e21ee1SDavid van Moolenbroek return len;
1325e4e21ee1SDavid van Moolenbroek }
1326e4e21ee1SDavid van Moolenbroek
1327e4e21ee1SDavid van Moolenbroek /*
1328e4e21ee1SDavid van Moolenbroek * Dispatch a sysctl call, by looking up the target node by its MIB name and
1329e4e21ee1SDavid van Moolenbroek * taking the appropriate action on the resulting node, if found. Return the
1330e4e21ee1SDavid van Moolenbroek * old data length on success, or a negative error code on failure.
1331e4e21ee1SDavid van Moolenbroek */
1332e4e21ee1SDavid van Moolenbroek ssize_t
mib_dispatch(struct mib_call * call,struct mib_oldp * oldp,struct mib_newp * newp)1333*6f3e0bcdSDavid van Moolenbroek mib_dispatch(struct mib_call * call, struct mib_oldp * oldp,
1334*6f3e0bcdSDavid van Moolenbroek struct mib_newp * newp)
1335e4e21ee1SDavid van Moolenbroek {
1336e4e21ee1SDavid van Moolenbroek struct mib_node *parent, *node;
1337*6f3e0bcdSDavid van Moolenbroek ssize_t r;
1338*6f3e0bcdSDavid van Moolenbroek int id, is_leaf, can_restart, has_verify, has_func;
1339e4e21ee1SDavid van Moolenbroek
1340e4e21ee1SDavid van Moolenbroek assert(call->call_namelen <= CTL_MAXNAME);
1341e4e21ee1SDavid van Moolenbroek
1342e4e21ee1SDavid van Moolenbroek /*
1343e4e21ee1SDavid van Moolenbroek * Resolve the name by descending into the node tree, level by level,
1344e4e21ee1SDavid van Moolenbroek * starting at the MIB root.
1345e4e21ee1SDavid van Moolenbroek */
1346*6f3e0bcdSDavid van Moolenbroek for (parent = &mib_root; call->call_namelen > 0; parent = node) {
1347e4e21ee1SDavid van Moolenbroek id = call->call_name[0];
1348e4e21ee1SDavid van Moolenbroek call->call_name++;
1349e4e21ee1SDavid van Moolenbroek call->call_namelen--;
1350e4e21ee1SDavid van Moolenbroek
1351e4e21ee1SDavid van Moolenbroek assert(SYSCTL_TYPE(parent->node_flags) == CTLTYPE_NODE);
1352e4e21ee1SDavid van Moolenbroek assert(parent->node_flags & CTLFLAG_PARENT);
1353e4e21ee1SDavid van Moolenbroek
1354e4e21ee1SDavid van Moolenbroek /*
1355e4e21ee1SDavid van Moolenbroek * Check for meta-identifiers. Regular identifiers are never
1356e4e21ee1SDavid van Moolenbroek * negative, although node handler functions may take subpaths
1357e4e21ee1SDavid van Moolenbroek * with negative identifiers that are not meta-identifiers
1358e4e21ee1SDavid van Moolenbroek * (e.g., see KERN_PROC2).
1359e4e21ee1SDavid van Moolenbroek */
1360e4e21ee1SDavid van Moolenbroek if (id < 0) {
1361e4e21ee1SDavid van Moolenbroek /*
1362e4e21ee1SDavid van Moolenbroek * A meta-identifier must always be the last name
1363e4e21ee1SDavid van Moolenbroek * component.
1364e4e21ee1SDavid van Moolenbroek */
1365e4e21ee1SDavid van Moolenbroek if (call->call_namelen > 0)
1366e4e21ee1SDavid van Moolenbroek return EINVAL;
1367e4e21ee1SDavid van Moolenbroek
1368e4e21ee1SDavid van Moolenbroek switch (id) {
1369e4e21ee1SDavid van Moolenbroek case CTL_QUERY:
1370*6f3e0bcdSDavid van Moolenbroek return mib_query(call, parent, oldp, newp);
1371e4e21ee1SDavid van Moolenbroek case CTL_CREATE:
1372*6f3e0bcdSDavid van Moolenbroek return mib_create(call, parent, oldp, newp);
1373e4e21ee1SDavid van Moolenbroek case CTL_DESTROY:
1374*6f3e0bcdSDavid van Moolenbroek return mib_destroy(call, parent, oldp, newp);
1375e4e21ee1SDavid van Moolenbroek case CTL_DESCRIBE:
1376e4e21ee1SDavid van Moolenbroek return mib_describe(call, parent, oldp, newp);
1377e4e21ee1SDavid van Moolenbroek case CTL_CREATESYM:
1378e4e21ee1SDavid van Moolenbroek case CTL_MMAP:
1379e4e21ee1SDavid van Moolenbroek default:
1380e4e21ee1SDavid van Moolenbroek return EOPNOTSUPP;
1381e4e21ee1SDavid van Moolenbroek }
1382e4e21ee1SDavid van Moolenbroek }
1383e4e21ee1SDavid van Moolenbroek
1384e4e21ee1SDavid van Moolenbroek /* Locate the child node. */
1385e4e21ee1SDavid van Moolenbroek if ((node = mib_find(parent, id, NULL /*prevp*/)) == NULL)
1386e4e21ee1SDavid van Moolenbroek return ENOENT;
1387e4e21ee1SDavid van Moolenbroek
1388e4e21ee1SDavid van Moolenbroek /* Check if access is permitted at this level. */
1389e4e21ee1SDavid van Moolenbroek if ((node->node_flags & CTLFLAG_PRIVATE) && !mib_authed(call))
1390e4e21ee1SDavid van Moolenbroek return EPERM;
1391e4e21ee1SDavid van Moolenbroek
1392e4e21ee1SDavid van Moolenbroek /*
1393*6f3e0bcdSDavid van Moolenbroek * Start by checking if the node is a remote node. If so, let
1394*6f3e0bcdSDavid van Moolenbroek * a remote service handle the remainder of this request.
1395*6f3e0bcdSDavid van Moolenbroek * However, as part of attempting the remote call, we may
1396*6f3e0bcdSDavid van Moolenbroek * discover that the remote service has died or that it is
1397*6f3e0bcdSDavid van Moolenbroek * unmounting the subtree. If the node was not a temporary
1398*6f3e0bcdSDavid van Moolenbroek * mountpoint, we should (and do) continue with the request
1399*6f3e0bcdSDavid van Moolenbroek * locally - if it was, it will already be deallocated and we
1400*6f3e0bcdSDavid van Moolenbroek * must be very careful not to access 'node' again!
1401*6f3e0bcdSDavid van Moolenbroek */
1402*6f3e0bcdSDavid van Moolenbroek is_leaf = (SYSCTL_TYPE(node->node_flags) != CTLTYPE_NODE);
1403*6f3e0bcdSDavid van Moolenbroek
1404*6f3e0bcdSDavid van Moolenbroek if (!is_leaf && (node->node_flags & CTLFLAG_REMOTE)) {
1405*6f3e0bcdSDavid van Moolenbroek /* Determine this before 'node' may disappear.. */
1406*6f3e0bcdSDavid van Moolenbroek can_restart = (node->node_flags & CTLFLAG_PARENT);
1407*6f3e0bcdSDavid van Moolenbroek
1408*6f3e0bcdSDavid van Moolenbroek r = mib_remote_call(call, node, oldp, newp);
1409*6f3e0bcdSDavid van Moolenbroek
1410*6f3e0bcdSDavid van Moolenbroek if (r != ERESTART || !can_restart)
1411*6f3e0bcdSDavid van Moolenbroek return (r != ERESTART) ? r : ENOENT;
1412*6f3e0bcdSDavid van Moolenbroek
1413*6f3e0bcdSDavid van Moolenbroek /* Service died, subtree is unmounted, keep going. */
1414*6f3e0bcdSDavid van Moolenbroek assert(SYSCTL_TYPE(node->node_flags) == CTLTYPE_NODE);
1415*6f3e0bcdSDavid van Moolenbroek assert(!(node->node_flags & CTLFLAG_REMOTE));
1416*6f3e0bcdSDavid van Moolenbroek }
1417*6f3e0bcdSDavid van Moolenbroek
1418*6f3e0bcdSDavid van Moolenbroek /*
1419e4e21ee1SDavid van Moolenbroek * Is this a leaf node, and/or is this node handled by a
1420e4e21ee1SDavid van Moolenbroek * function? If either is true, resolution ends at this level.
1421e4e21ee1SDavid van Moolenbroek * In order to save a few bytes of memory per node, we use
1422e4e21ee1SDavid van Moolenbroek * different ways to determine whether there is a function
1423e4e21ee1SDavid van Moolenbroek * depending on whether the node is a leaf or not.
1424e4e21ee1SDavid van Moolenbroek */
1425e4e21ee1SDavid van Moolenbroek if (is_leaf) {
1426e4e21ee1SDavid van Moolenbroek has_verify = (node->node_flags & CTLFLAG_VERIFY);
1427e4e21ee1SDavid van Moolenbroek has_func = (!has_verify && node->node_func != NULL);
1428e4e21ee1SDavid van Moolenbroek } else {
1429e4e21ee1SDavid van Moolenbroek has_verify = FALSE;
1430e4e21ee1SDavid van Moolenbroek has_func = !(node->node_flags & CTLFLAG_PARENT);
1431e4e21ee1SDavid van Moolenbroek }
1432e4e21ee1SDavid van Moolenbroek
1433e4e21ee1SDavid van Moolenbroek /*
1434e4e21ee1SDavid van Moolenbroek * The name may be longer only if the node is not a leaf. That
1435e4e21ee1SDavid van Moolenbroek * also applies to leaves with functions, so check this first.
1436e4e21ee1SDavid van Moolenbroek */
1437e4e21ee1SDavid van Moolenbroek if (is_leaf && call->call_namelen > 0)
1438e4e21ee1SDavid van Moolenbroek return ENOTDIR;
1439e4e21ee1SDavid van Moolenbroek
1440e4e21ee1SDavid van Moolenbroek /*
1441e4e21ee1SDavid van Moolenbroek * If resolution indeed ends here, and the user supplied new
1442e4e21ee1SDavid van Moolenbroek * data, check if writing is allowed. For functions, it is
1443e4e21ee1SDavid van Moolenbroek * arguable whether we should do this check here already.
1444e4e21ee1SDavid van Moolenbroek * However, for now, this approach covers all our use cases.
1445e4e21ee1SDavid van Moolenbroek */
1446e4e21ee1SDavid van Moolenbroek if ((is_leaf || has_func) && newp != NULL) {
1447e4e21ee1SDavid van Moolenbroek if (!(node->node_flags & CTLFLAG_READWRITE))
1448e4e21ee1SDavid van Moolenbroek return EPERM;
1449e4e21ee1SDavid van Moolenbroek
1450e4e21ee1SDavid van Moolenbroek /*
1451e4e21ee1SDavid van Moolenbroek * Unless nonprivileged users may write to this node,
1452e4e21ee1SDavid van Moolenbroek * ensure that the user has superuser privileges. The
1453e4e21ee1SDavid van Moolenbroek * ANYWRITE flag does not override the READWRITE flag.
1454e4e21ee1SDavid van Moolenbroek */
1455e4e21ee1SDavid van Moolenbroek if (!(node->node_flags & CTLFLAG_ANYWRITE) &&
1456e4e21ee1SDavid van Moolenbroek !mib_authed(call))
1457e4e21ee1SDavid van Moolenbroek return EPERM;
1458e4e21ee1SDavid van Moolenbroek }
1459e4e21ee1SDavid van Moolenbroek
1460e4e21ee1SDavid van Moolenbroek /* If this node has a handler function, let it do the work. */
1461e4e21ee1SDavid van Moolenbroek if (has_func)
1462e4e21ee1SDavid van Moolenbroek return node->node_func(call, node, oldp, newp);
1463e4e21ee1SDavid van Moolenbroek
1464e4e21ee1SDavid van Moolenbroek /* For regular data leaf nodes, handle generic access. */
1465e4e21ee1SDavid van Moolenbroek if (is_leaf)
1466e4e21ee1SDavid van Moolenbroek return mib_readwrite(call, node, oldp, newp,
1467e4e21ee1SDavid van Moolenbroek has_verify ? node->node_verify : NULL);
1468e4e21ee1SDavid van Moolenbroek
1469e4e21ee1SDavid van Moolenbroek /* No function and not a leaf? Descend further. */
1470e4e21ee1SDavid van Moolenbroek }
1471e4e21ee1SDavid van Moolenbroek
1472e4e21ee1SDavid van Moolenbroek /* If we get here, the name refers to a node array. */
1473e4e21ee1SDavid van Moolenbroek return EISDIR;
1474e4e21ee1SDavid van Moolenbroek }
1475e4e21ee1SDavid van Moolenbroek
1476e4e21ee1SDavid van Moolenbroek /*
1477e4e21ee1SDavid van Moolenbroek * Recursively initialize the static tree at initialization time.
1478e4e21ee1SDavid van Moolenbroek */
1479e4e21ee1SDavid van Moolenbroek static void
mib_tree_recurse(struct mib_node * parent)1480e4e21ee1SDavid van Moolenbroek mib_tree_recurse(struct mib_node * parent)
1481e4e21ee1SDavid van Moolenbroek {
1482e4e21ee1SDavid van Moolenbroek struct mib_node *node;
1483e4e21ee1SDavid van Moolenbroek int id;
1484e4e21ee1SDavid van Moolenbroek
1485e4e21ee1SDavid van Moolenbroek assert(SYSCTL_TYPE(parent->node_flags) == CTLTYPE_NODE);
1486e4e21ee1SDavid van Moolenbroek assert(parent->node_flags & CTLFLAG_PARENT);
1487e4e21ee1SDavid van Moolenbroek
1488e4e21ee1SDavid van Moolenbroek /*
1489e4e21ee1SDavid van Moolenbroek * Later on, node_csize and node_clen will also include dynamically
1490e4e21ee1SDavid van Moolenbroek * created nodes. This means that we cannot use node_csize to iterate
1491e4e21ee1SDavid van Moolenbroek * over the static nodes.
1492e4e21ee1SDavid van Moolenbroek */
1493e4e21ee1SDavid van Moolenbroek parent->node_csize = parent->node_size;
1494e4e21ee1SDavid van Moolenbroek
1495e4e21ee1SDavid van Moolenbroek node = parent->node_scptr;
1496e4e21ee1SDavid van Moolenbroek
1497e4e21ee1SDavid van Moolenbroek for (id = 0; IS_STATIC_ID(parent, id); id++, node++) {
1498e4e21ee1SDavid van Moolenbroek if (node->node_flags == 0)
1499e4e21ee1SDavid van Moolenbroek continue;
1500e4e21ee1SDavid van Moolenbroek
1501*6f3e0bcdSDavid van Moolenbroek mib_nodes++;
1502e4e21ee1SDavid van Moolenbroek
1503e4e21ee1SDavid van Moolenbroek parent->node_clen++;
1504e4e21ee1SDavid van Moolenbroek
1505e4e21ee1SDavid van Moolenbroek node->node_ver = parent->node_ver;
1506*6f3e0bcdSDavid van Moolenbroek node->node_parent = parent;
1507e4e21ee1SDavid van Moolenbroek
1508e4e21ee1SDavid van Moolenbroek /* Recursively apply this function to all node children. */
1509e4e21ee1SDavid van Moolenbroek if (SYSCTL_TYPE(node->node_flags) == CTLTYPE_NODE &&
1510e4e21ee1SDavid van Moolenbroek (node->node_flags & CTLFLAG_PARENT))
1511e4e21ee1SDavid van Moolenbroek mib_tree_recurse(node);
1512e4e21ee1SDavid van Moolenbroek }
1513e4e21ee1SDavid van Moolenbroek }
1514e4e21ee1SDavid van Moolenbroek
1515e4e21ee1SDavid van Moolenbroek /*
1516e4e21ee1SDavid van Moolenbroek * Go through the entire static tree, recursively, initializing some values
1517e4e21ee1SDavid van Moolenbroek * that could not be assigned at compile time.
1518e4e21ee1SDavid van Moolenbroek */
1519e4e21ee1SDavid van Moolenbroek void
mib_tree_init(void)1520*6f3e0bcdSDavid van Moolenbroek mib_tree_init(void)
1521e4e21ee1SDavid van Moolenbroek {
1522e4e21ee1SDavid van Moolenbroek
1523e4e21ee1SDavid van Moolenbroek /* Initialize some variables. */
1524*6f3e0bcdSDavid van Moolenbroek mib_nodes = 1; /* the root node itself */
1525*6f3e0bcdSDavid van Moolenbroek mib_objects = 0;
1526e4e21ee1SDavid van Moolenbroek
1527*6f3e0bcdSDavid van Moolenbroek /*
1528*6f3e0bcdSDavid van Moolenbroek * The entire tree starts with the same, nonzero node version.
1529*6f3e0bcdSDavid van Moolenbroek * The root node is the only node without a parent.
1530*6f3e0bcdSDavid van Moolenbroek */
1531*6f3e0bcdSDavid van Moolenbroek mib_root.node_ver = 1;
1532*6f3e0bcdSDavid van Moolenbroek mib_root.node_parent = NULL;
1533e4e21ee1SDavid van Moolenbroek
1534e4e21ee1SDavid van Moolenbroek /* Recursively initialize the static tree. */
1535*6f3e0bcdSDavid van Moolenbroek mib_tree_recurse(&mib_root);
1536*6f3e0bcdSDavid van Moolenbroek }
1537*6f3e0bcdSDavid van Moolenbroek
1538*6f3e0bcdSDavid van Moolenbroek /*
1539*6f3e0bcdSDavid van Moolenbroek * Process a subtree mount request from a remote service. Return OK on
1540*6f3e0bcdSDavid van Moolenbroek * success, with a pointer to the resulting static-node structure stored in
1541*6f3e0bcdSDavid van Moolenbroek * 'nodep'. Return a negative error code on failure.
1542*6f3e0bcdSDavid van Moolenbroek */
1543*6f3e0bcdSDavid van Moolenbroek int
mib_mount(const int * mib,unsigned int miblen,unsigned int eid,uint32_t rid,uint32_t flags,unsigned int csize,unsigned int clen,struct mib_node ** nodep)1544*6f3e0bcdSDavid van Moolenbroek mib_mount(const int * mib, unsigned int miblen, unsigned int eid, uint32_t rid,
1545*6f3e0bcdSDavid van Moolenbroek uint32_t flags, unsigned int csize, unsigned int clen,
1546*6f3e0bcdSDavid van Moolenbroek struct mib_node ** nodep)
1547*6f3e0bcdSDavid van Moolenbroek {
1548*6f3e0bcdSDavid van Moolenbroek struct mib_dynode *dynode, **prevp;
1549*6f3e0bcdSDavid van Moolenbroek struct mib_node *parent, *node;
1550*6f3e0bcdSDavid van Moolenbroek char name[SYSCTL_NAMELEN], *desc;
1551*6f3e0bcdSDavid van Moolenbroek size_t size, namelen, desclen;
1552*6f3e0bcdSDavid van Moolenbroek unsigned int n;
1553*6f3e0bcdSDavid van Moolenbroek int r, id;
1554*6f3e0bcdSDavid van Moolenbroek
1555*6f3e0bcdSDavid van Moolenbroek /*
1556*6f3e0bcdSDavid van Moolenbroek * Perform initial verification of the given parameters. Even stricter
1557*6f3e0bcdSDavid van Moolenbroek * checks may be performed later.
1558*6f3e0bcdSDavid van Moolenbroek */
1559*6f3e0bcdSDavid van Moolenbroek /*
1560*6f3e0bcdSDavid van Moolenbroek * By policy, we forbid mounting top-level nodes. This is in effect
1561*6f3e0bcdSDavid van Moolenbroek * also the only security-like restriction: a service should not be
1562*6f3e0bcdSDavid van Moolenbroek * able to just take over, say, the entire "kern" subtree. There is
1563*6f3e0bcdSDavid van Moolenbroek * currently little in the way of a service taking over an important
1564*6f3e0bcdSDavid van Moolenbroek * set of second-level nodes, though.
1565*6f3e0bcdSDavid van Moolenbroek *
1566*6f3e0bcdSDavid van Moolenbroek * TODO: allow mounting of predefined mount points only, for example by
1567*6f3e0bcdSDavid van Moolenbroek * having an internal node flag that permits mounting the subtree or
1568*6f3e0bcdSDavid van Moolenbroek * any node in it. As an even better alternative, allow this to be
1569*6f3e0bcdSDavid van Moolenbroek * controlled through a policy specification; unfortunately, this would
1570*6f3e0bcdSDavid van Moolenbroek * also add a substantial amount of infrastructure.
1571*6f3e0bcdSDavid van Moolenbroek */
1572*6f3e0bcdSDavid van Moolenbroek if (miblen < 2) {
1573*6f3e0bcdSDavid van Moolenbroek MIB_DEBUG_MOUNT(("MIB: mounting failed, path too short\n"));
1574*6f3e0bcdSDavid van Moolenbroek
1575*6f3e0bcdSDavid van Moolenbroek return EPERM;
1576*6f3e0bcdSDavid van Moolenbroek }
1577*6f3e0bcdSDavid van Moolenbroek
1578*6f3e0bcdSDavid van Moolenbroek /*
1579*6f3e0bcdSDavid van Moolenbroek * The flags field is highly restricted right now. Only a few flags
1580*6f3e0bcdSDavid van Moolenbroek * may be given at all, and then when using an existing node as mount
1581*6f3e0bcdSDavid van Moolenbroek * point, the flag must exactly match the existing node's flags.
1582*6f3e0bcdSDavid van Moolenbroek */
1583*6f3e0bcdSDavid van Moolenbroek if (SYSCTL_VERS(flags) != SYSCTL_VERSION ||
1584*6f3e0bcdSDavid van Moolenbroek SYSCTL_TYPE(flags) != CTLTYPE_NODE ||
1585*6f3e0bcdSDavid van Moolenbroek (SYSCTL_FLAGS(flags) & ~(CTLFLAG_READONLY | CTLFLAG_READWRITE |
1586*6f3e0bcdSDavid van Moolenbroek CTLFLAG_PERMANENT | CTLFLAG_HIDDEN)) != 0) {
1587*6f3e0bcdSDavid van Moolenbroek MIB_DEBUG_MOUNT(("MIB: mounting failed, invalid flags %"PRIx32
1588*6f3e0bcdSDavid van Moolenbroek "\n", flags));
1589*6f3e0bcdSDavid van Moolenbroek
1590*6f3e0bcdSDavid van Moolenbroek return EINVAL;
1591*6f3e0bcdSDavid van Moolenbroek }
1592*6f3e0bcdSDavid van Moolenbroek
1593*6f3e0bcdSDavid van Moolenbroek if (csize > (1U << MIB_RC_BITS) || clen > csize) {
1594*6f3e0bcdSDavid van Moolenbroek MIB_DEBUG_MOUNT(("MIB: mounting failed, invalid child size or "
1595*6f3e0bcdSDavid van Moolenbroek "length (%u, %u)\n", csize, clen));
1596*6f3e0bcdSDavid van Moolenbroek
1597*6f3e0bcdSDavid van Moolenbroek return EINVAL;
1598*6f3e0bcdSDavid van Moolenbroek }
1599*6f3e0bcdSDavid van Moolenbroek
1600*6f3e0bcdSDavid van Moolenbroek /*
1601*6f3e0bcdSDavid van Moolenbroek * Look up the parent node of the mount point. This parent node must
1602*6f3e0bcdSDavid van Moolenbroek * exist - we don't want to create more than one temporary node in any
1603*6f3e0bcdSDavid van Moolenbroek * case. All the nodes leading up to and including the parent node
1604*6f3e0bcdSDavid van Moolenbroek * must be real, local, non-private, node-type nodes. The path may not
1605*6f3e0bcdSDavid van Moolenbroek * be private, because that would allow an unprivileged service to
1606*6f3e0bcdSDavid van Moolenbroek * intercept writes to privileged nodes--currently a total nonissue in
1607*6f3e0bcdSDavid van Moolenbroek * practice, but still. Note that the service may itself restrict
1608*6f3e0bcdSDavid van Moolenbroek * access to nodes in its own mounted subtree in any way it wishes.
1609*6f3e0bcdSDavid van Moolenbroek */
1610*6f3e0bcdSDavid van Moolenbroek parent = &mib_root;
1611*6f3e0bcdSDavid van Moolenbroek
1612*6f3e0bcdSDavid van Moolenbroek for (n = 0; n < miblen - 1; n++) {
1613*6f3e0bcdSDavid van Moolenbroek /* Meta-identifiers are obviously not allowed in the path. */
1614*6f3e0bcdSDavid van Moolenbroek if ((id = mib[n]) < 0) {
1615*6f3e0bcdSDavid van Moolenbroek MIB_DEBUG_MOUNT(("MIB: mounting failed, meta-ID in "
1616*6f3e0bcdSDavid van Moolenbroek "path\n"));
1617*6f3e0bcdSDavid van Moolenbroek
1618*6f3e0bcdSDavid van Moolenbroek return EINVAL;
1619*6f3e0bcdSDavid van Moolenbroek }
1620*6f3e0bcdSDavid van Moolenbroek
1621*6f3e0bcdSDavid van Moolenbroek /* Locate the child node. */
1622*6f3e0bcdSDavid van Moolenbroek if ((node = mib_find(parent, id, NULL /*prevp*/)) == NULL) {
1623*6f3e0bcdSDavid van Moolenbroek MIB_DEBUG_MOUNT(("MIB: mounting failed, path not "
1624*6f3e0bcdSDavid van Moolenbroek "found\n"));
1625*6f3e0bcdSDavid van Moolenbroek
1626*6f3e0bcdSDavid van Moolenbroek return ENOENT;
1627*6f3e0bcdSDavid van Moolenbroek }
1628*6f3e0bcdSDavid van Moolenbroek
1629*6f3e0bcdSDavid van Moolenbroek /* Make sure it is a regular node-type node. */
1630*6f3e0bcdSDavid van Moolenbroek if (SYSCTL_TYPE(node->node_flags) != CTLTYPE_NODE ||
1631*6f3e0bcdSDavid van Moolenbroek !(node->node_flags & CTLFLAG_PARENT) ||
1632*6f3e0bcdSDavid van Moolenbroek (node->node_flags & (CTLFLAG_REMOTE | CTLFLAG_PRIVATE))) {
1633*6f3e0bcdSDavid van Moolenbroek MIB_DEBUG_MOUNT(("MIB: mounting failed, unacceptable "
1634*6f3e0bcdSDavid van Moolenbroek "node on path\n"));
1635*6f3e0bcdSDavid van Moolenbroek
1636*6f3e0bcdSDavid van Moolenbroek return EPERM;
1637*6f3e0bcdSDavid van Moolenbroek }
1638*6f3e0bcdSDavid van Moolenbroek
1639*6f3e0bcdSDavid van Moolenbroek parent = node;
1640*6f3e0bcdSDavid van Moolenbroek }
1641*6f3e0bcdSDavid van Moolenbroek
1642*6f3e0bcdSDavid van Moolenbroek /* Now see if the mount point itself exists. */
1643*6f3e0bcdSDavid van Moolenbroek if ((id = mib[miblen - 1]) < 0) {
1644*6f3e0bcdSDavid van Moolenbroek MIB_DEBUG_MOUNT(("MIB: mounting failed, meta-ID in path\n"));
1645*6f3e0bcdSDavid van Moolenbroek
1646*6f3e0bcdSDavid van Moolenbroek return EINVAL;
1647*6f3e0bcdSDavid van Moolenbroek }
1648*6f3e0bcdSDavid van Moolenbroek
1649*6f3e0bcdSDavid van Moolenbroek /*
1650*6f3e0bcdSDavid van Moolenbroek * If the target node exists and passes all tests, it will simply be
1651*6f3e0bcdSDavid van Moolenbroek * converted to a mount point. If the target node does not exist, we
1652*6f3e0bcdSDavid van Moolenbroek * have to allocate a temporary node as mount point.
1653*6f3e0bcdSDavid van Moolenbroek */
1654*6f3e0bcdSDavid van Moolenbroek if ((node = mib_find(parent, id, NULL /*prevp*/)) != NULL) {
1655*6f3e0bcdSDavid van Moolenbroek /*
1656*6f3e0bcdSDavid van Moolenbroek * We are about to mount on an existing node. As stated above,
1657*6f3e0bcdSDavid van Moolenbroek * the node flags must match the given flags exactly.
1658*6f3e0bcdSDavid van Moolenbroek */
1659*6f3e0bcdSDavid van Moolenbroek if (SYSCTL_TYPE(node->node_flags) != CTLTYPE_NODE ||
1660*6f3e0bcdSDavid van Moolenbroek SYSCTL_FLAGS(node->node_flags) !=
1661*6f3e0bcdSDavid van Moolenbroek (SYSCTL_FLAGS(flags) | CTLFLAG_PARENT)) {
1662*6f3e0bcdSDavid van Moolenbroek MIB_DEBUG_MOUNT(("MIB: mounting failed, target node "
1663*6f3e0bcdSDavid van Moolenbroek "mismatch (%"PRIx32", %"PRIx32")\n",
1664*6f3e0bcdSDavid van Moolenbroek node->node_flags, flags));
1665*6f3e0bcdSDavid van Moolenbroek
1666*6f3e0bcdSDavid van Moolenbroek return EPERM;
1667*6f3e0bcdSDavid van Moolenbroek }
1668*6f3e0bcdSDavid van Moolenbroek
1669*6f3e0bcdSDavid van Moolenbroek /*
1670*6f3e0bcdSDavid van Moolenbroek * If the node has dynamically added children, we will not be
1671*6f3e0bcdSDavid van Moolenbroek * able to restore the node to its old state when unmounting.
1672*6f3e0bcdSDavid van Moolenbroek */
1673*6f3e0bcdSDavid van Moolenbroek if (node->node_size != node->node_csize) {
1674*6f3e0bcdSDavid van Moolenbroek MIB_DEBUG_MOUNT(("MIB: mounting failed, node has "
1675*6f3e0bcdSDavid van Moolenbroek "dynamic children\n"));
1676*6f3e0bcdSDavid van Moolenbroek
1677*6f3e0bcdSDavid van Moolenbroek return EBUSY;
1678*6f3e0bcdSDavid van Moolenbroek }
1679*6f3e0bcdSDavid van Moolenbroek
1680*6f3e0bcdSDavid van Moolenbroek mib_upgrade(node);
1681*6f3e0bcdSDavid van Moolenbroek } else {
1682*6f3e0bcdSDavid van Moolenbroek /*
1683*6f3e0bcdSDavid van Moolenbroek * We are going to create a temporary mount point. Much of the
1684*6f3e0bcdSDavid van Moolenbroek * procedure that follows is a rather selective extract from
1685*6f3e0bcdSDavid van Moolenbroek * mib_create(). Start with a check for the impossible.
1686*6f3e0bcdSDavid van Moolenbroek */
1687*6f3e0bcdSDavid van Moolenbroek if (parent->node_csize == INT_MAX) {
1688*6f3e0bcdSDavid van Moolenbroek MIB_DEBUG_MOUNT(("MIB: mounting failed, parent node "
1689*6f3e0bcdSDavid van Moolenbroek "full\n"));
1690*6f3e0bcdSDavid van Moolenbroek
1691*6f3e0bcdSDavid van Moolenbroek return EINVAL;
1692*6f3e0bcdSDavid van Moolenbroek }
1693*6f3e0bcdSDavid van Moolenbroek
1694*6f3e0bcdSDavid van Moolenbroek /*
1695*6f3e0bcdSDavid van Moolenbroek * In order to create the new node, we also need the node's
1696*6f3e0bcdSDavid van Moolenbroek * name and description; those did not fit in the request
1697*6f3e0bcdSDavid van Moolenbroek * message. Ask the caller to copy these strings to us.
1698*6f3e0bcdSDavid van Moolenbroek */
1699*6f3e0bcdSDavid van Moolenbroek name[0] = '\0';
1700*6f3e0bcdSDavid van Moolenbroek scratch[0] = '\0';
1701*6f3e0bcdSDavid van Moolenbroek
1702*6f3e0bcdSDavid van Moolenbroek if ((r = mib_remote_info(eid, rid, name, sizeof(name), scratch,
1703*6f3e0bcdSDavid van Moolenbroek MAXDESCLEN)) != OK) {
1704*6f3e0bcdSDavid van Moolenbroek MIB_DEBUG_MOUNT(("MIB: mounting failed, node info "
1705*6f3e0bcdSDavid van Moolenbroek "request yielded %d\n", r));
1706*6f3e0bcdSDavid van Moolenbroek
1707*6f3e0bcdSDavid van Moolenbroek return r;
1708*6f3e0bcdSDavid van Moolenbroek }
1709*6f3e0bcdSDavid van Moolenbroek
1710*6f3e0bcdSDavid van Moolenbroek /* Make sure the name is valid. */
1711*6f3e0bcdSDavid van Moolenbroek if ((namelen = mib_check_name(name, sizeof(name))) == 0) {
1712*6f3e0bcdSDavid van Moolenbroek printf("MIB: mounting failed, bad name\n");
1713*6f3e0bcdSDavid van Moolenbroek
1714*6f3e0bcdSDavid van Moolenbroek return EINVAL;
1715*6f3e0bcdSDavid van Moolenbroek }
1716*6f3e0bcdSDavid van Moolenbroek
1717*6f3e0bcdSDavid van Moolenbroek /* Just forcefully terminate the description. */
1718*6f3e0bcdSDavid van Moolenbroek scratch[MAXDESCLEN - 1] = '\0';
1719*6f3e0bcdSDavid van Moolenbroek desclen = strlen(scratch);
1720*6f3e0bcdSDavid van Moolenbroek
1721*6f3e0bcdSDavid van Moolenbroek /*
1722*6f3e0bcdSDavid van Moolenbroek * We know the identifier is not in use yet; make sure that the
1723*6f3e0bcdSDavid van Moolenbroek * name is not, either. As a side effect, find out where the
1724*6f3e0bcdSDavid van Moolenbroek * new node should be inserted upon success.
1725*6f3e0bcdSDavid van Moolenbroek */
1726*6f3e0bcdSDavid van Moolenbroek if (mib_scan(parent, id, name, &id /*unused*/, &prevp,
1727*6f3e0bcdSDavid van Moolenbroek &node /*unused*/) != OK) {
1728*6f3e0bcdSDavid van Moolenbroek MIB_DEBUG_MOUNT(("MIB: mounting failed, name "
1729*6f3e0bcdSDavid van Moolenbroek "conflict\n"));
1730*6f3e0bcdSDavid van Moolenbroek
1731*6f3e0bcdSDavid van Moolenbroek return EEXIST;
1732*6f3e0bcdSDavid van Moolenbroek }
1733*6f3e0bcdSDavid van Moolenbroek
1734*6f3e0bcdSDavid van Moolenbroek /*
1735*6f3e0bcdSDavid van Moolenbroek * Allocate a dynamic node. Unlike for user-created dynamic
1736*6f3e0bcdSDavid van Moolenbroek * nodes, temporary mount points also include the description
1737*6f3e0bcdSDavid van Moolenbroek * in the dynode object.
1738*6f3e0bcdSDavid van Moolenbroek */
1739*6f3e0bcdSDavid van Moolenbroek size = sizeof(*dynode) + namelen + desclen + 1;
1740*6f3e0bcdSDavid van Moolenbroek
1741*6f3e0bcdSDavid van Moolenbroek if ((dynode = malloc(size)) == NULL) {
1742*6f3e0bcdSDavid van Moolenbroek printf("MIB: out of memory!\n");
1743*6f3e0bcdSDavid van Moolenbroek
1744*6f3e0bcdSDavid van Moolenbroek return ENOMEM;
1745*6f3e0bcdSDavid van Moolenbroek }
1746*6f3e0bcdSDavid van Moolenbroek mib_objects++;
1747*6f3e0bcdSDavid van Moolenbroek
1748*6f3e0bcdSDavid van Moolenbroek /* Initialize the dynamic node. */
1749*6f3e0bcdSDavid van Moolenbroek memset(dynode, 0, sizeof(*dynode));
1750*6f3e0bcdSDavid van Moolenbroek dynode->dynode_id = id;
1751*6f3e0bcdSDavid van Moolenbroek strlcpy(dynode->dynode_name, name, namelen + 1);
1752*6f3e0bcdSDavid van Moolenbroek desc = &dynode->dynode_name[namelen + 1];
1753*6f3e0bcdSDavid van Moolenbroek strlcpy(desc, scratch, desclen + 1);
1754*6f3e0bcdSDavid van Moolenbroek
1755*6f3e0bcdSDavid van Moolenbroek node = &dynode->dynode_node;
1756*6f3e0bcdSDavid van Moolenbroek node->node_flags = flags & ~SYSCTL_VERS_MASK;
1757*6f3e0bcdSDavid van Moolenbroek node->node_size = 0;
1758*6f3e0bcdSDavid van Moolenbroek node->node_parent = parent;
1759*6f3e0bcdSDavid van Moolenbroek node->node_name = dynode->dynode_name;
1760*6f3e0bcdSDavid van Moolenbroek node->node_desc = desc;
1761*6f3e0bcdSDavid van Moolenbroek
1762*6f3e0bcdSDavid van Moolenbroek /*
1763*6f3e0bcdSDavid van Moolenbroek * Add the new dynamic node into the tree, and adjust versions
1764*6f3e0bcdSDavid van Moolenbroek * and counters.
1765*6f3e0bcdSDavid van Moolenbroek */
1766*6f3e0bcdSDavid van Moolenbroek mib_add(dynode, prevp);
1767*6f3e0bcdSDavid van Moolenbroek }
1768*6f3e0bcdSDavid van Moolenbroek
1769*6f3e0bcdSDavid van Moolenbroek /* Success! Perform the actual mount, and return the target node. */
1770*6f3e0bcdSDavid van Moolenbroek node->node_flags |= CTLFLAG_REMOTE;
1771*6f3e0bcdSDavid van Moolenbroek node->node_eid = eid;
1772*6f3e0bcdSDavid van Moolenbroek node->node_rcsize = csize;
1773*6f3e0bcdSDavid van Moolenbroek node->node_rclen = clen;
1774*6f3e0bcdSDavid van Moolenbroek node->node_rid = rid;
1775*6f3e0bcdSDavid van Moolenbroek
1776*6f3e0bcdSDavid van Moolenbroek mib_remotes++;
1777*6f3e0bcdSDavid van Moolenbroek
1778*6f3e0bcdSDavid van Moolenbroek *nodep = node;
1779*6f3e0bcdSDavid van Moolenbroek return OK;
1780*6f3e0bcdSDavid van Moolenbroek }
1781*6f3e0bcdSDavid van Moolenbroek
1782*6f3e0bcdSDavid van Moolenbroek /*
1783*6f3e0bcdSDavid van Moolenbroek * Unmount the remote subtree identified by the given node. Release the mount
1784*6f3e0bcdSDavid van Moolenbroek * point by reversing the action performed while mounting. Also bump the
1785*6f3e0bcdSDavid van Moolenbroek * version numbers on the path, so that userland knows that it is to expect a
1786*6f3e0bcdSDavid van Moolenbroek * change of contents in the subtree. This function always succeeds, and may
1787*6f3e0bcdSDavid van Moolenbroek * deallocate the given node.
1788*6f3e0bcdSDavid van Moolenbroek */
1789*6f3e0bcdSDavid van Moolenbroek void
mib_unmount(struct mib_node * node)1790*6f3e0bcdSDavid van Moolenbroek mib_unmount(struct mib_node * node)
1791*6f3e0bcdSDavid van Moolenbroek {
1792*6f3e0bcdSDavid van Moolenbroek struct mib_dynode **prevp;
1793*6f3e0bcdSDavid van Moolenbroek struct mib_node *child;
1794*6f3e0bcdSDavid van Moolenbroek int id;
1795*6f3e0bcdSDavid van Moolenbroek
1796*6f3e0bcdSDavid van Moolenbroek assert(SYSCTL_TYPE(node->node_flags) == CTLTYPE_NODE);
1797*6f3e0bcdSDavid van Moolenbroek assert(node->node_flags & CTLFLAG_REMOTE);
1798*6f3e0bcdSDavid van Moolenbroek
1799*6f3e0bcdSDavid van Moolenbroek /*
1800*6f3e0bcdSDavid van Moolenbroek * Given that the node has the CTLFLAG_REMOTE flag set, we can now tell
1801*6f3e0bcdSDavid van Moolenbroek * whether the remote subtree obscured a preexisting node or we created
1802*6f3e0bcdSDavid van Moolenbroek * a temporary mount point, by checking its CTLFLAG_PARENT flag.
1803*6f3e0bcdSDavid van Moolenbroek */
1804*6f3e0bcdSDavid van Moolenbroek if (node->node_flags & CTLFLAG_PARENT) {
1805*6f3e0bcdSDavid van Moolenbroek /*
1806*6f3e0bcdSDavid van Moolenbroek * Return the node to its former pre-mount state. Restore the
1807*6f3e0bcdSDavid van Moolenbroek * original node_clen field by recomputing it.
1808*6f3e0bcdSDavid van Moolenbroek */
1809*6f3e0bcdSDavid van Moolenbroek node->node_flags &= ~CTLFLAG_REMOTE;
1810*6f3e0bcdSDavid van Moolenbroek node->node_csize = node->node_size;
1811*6f3e0bcdSDavid van Moolenbroek node->node_clen = 0;
1812*6f3e0bcdSDavid van Moolenbroek
1813*6f3e0bcdSDavid van Moolenbroek for (id = 0; IS_STATIC_ID(node, id); id++) {
1814*6f3e0bcdSDavid van Moolenbroek child = &node->node_scptr[id];
1815*6f3e0bcdSDavid van Moolenbroek
1816*6f3e0bcdSDavid van Moolenbroek if (child->node_flags != 0)
1817*6f3e0bcdSDavid van Moolenbroek node->node_clen++;
1818*6f3e0bcdSDavid van Moolenbroek }
1819*6f3e0bcdSDavid van Moolenbroek
1820*6f3e0bcdSDavid van Moolenbroek node->node_dcptr = NULL;
1821*6f3e0bcdSDavid van Moolenbroek
1822*6f3e0bcdSDavid van Moolenbroek /* Increase version numbers on the path to the node. */
1823*6f3e0bcdSDavid van Moolenbroek mib_upgrade(node);
1824*6f3e0bcdSDavid van Moolenbroek } else {
1825*6f3e0bcdSDavid van Moolenbroek /*
1826*6f3e0bcdSDavid van Moolenbroek * We know that we dynamically allocated this node; find its
1827*6f3e0bcdSDavid van Moolenbroek * parent's pointer to it.
1828*6f3e0bcdSDavid van Moolenbroek */
1829*6f3e0bcdSDavid van Moolenbroek for (prevp = &node->node_parent->node_dcptr; *prevp != NULL;
1830*6f3e0bcdSDavid van Moolenbroek prevp = &(*prevp)->dynode_next) {
1831*6f3e0bcdSDavid van Moolenbroek if (&(*prevp)->dynode_node == node)
1832*6f3e0bcdSDavid van Moolenbroek break;
1833*6f3e0bcdSDavid van Moolenbroek }
1834*6f3e0bcdSDavid van Moolenbroek assert(*prevp != NULL);
1835*6f3e0bcdSDavid van Moolenbroek
1836*6f3e0bcdSDavid van Moolenbroek /* Free the node, and adjust counts and versions. */
1837*6f3e0bcdSDavid van Moolenbroek mib_remove(node, prevp);
1838*6f3e0bcdSDavid van Moolenbroek }
1839*6f3e0bcdSDavid van Moolenbroek
1840*6f3e0bcdSDavid van Moolenbroek assert(mib_remotes > 0);
1841*6f3e0bcdSDavid van Moolenbroek mib_remotes--;
1842e4e21ee1SDavid van Moolenbroek }
1843