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