+ int smallValue;
+ int error;
+
+ if (changed) {
+ *changed = 0;
+ }
+
+ /*
+ * Handle the various combinations of caller buffer size and
+ * data value size. We are generous in the case where the
+ * caller has specified a 32-bit buffer but the value is 64-bit
+ * sized.
+ */
+
+ /* 32 bit value expected or 32 bit buffer offered */
+ if (((valueSize == sizeof(int)) ||
+ ((req->oldlen == sizeof(int)) && (valueSize == sizeof(long long))))
+ && (req->oldptr)) {
+ smallValue = (int)bigValue;
+ if ((long long)smallValue != bigValue) {
+ return ERANGE;
+ }
+ error = SYSCTL_OUT(req, &smallValue, sizeof(smallValue));
+ } else {
+ /* any other case is either size-equal or a bug */
+ error = SYSCTL_OUT(req, &bigValue, valueSize);
+ }
+ /* error or nothing to set */
+ if (error || !req->newptr) {
+ return error;
+ }
+
+ /* set request for constant */
+ if (pValue == NULL) {
+ return EPERM;
+ }
+
+ /* set request needs to convert? */
+ if ((req->newlen == sizeof(int)) && (valueSize == sizeof(long long))) {
+ /* new value is 32 bits, upconvert to 64 bits */
+ error = SYSCTL_IN(req, &smallValue, sizeof(smallValue));
+ if (!error) {
+ *(long long *)pValue = (long long)smallValue;
+ }
+ } else if ((req->newlen == sizeof(long long)) && (valueSize == sizeof(int))) {
+ /* new value is 64 bits, downconvert to 32 bits and range check */
+ error = SYSCTL_IN(req, &bigValue, sizeof(bigValue));
+ if (!error) {
+ smallValue = (int)bigValue;
+ if ((long long)smallValue != bigValue) {
+ return ERANGE;
+ }
+ *(int *)pValue = smallValue;
+ }
+ } else {
+ /* sizes match, just copy in */
+ error = SYSCTL_IN(req, pValue, valueSize);
+ }
+ if (!error && changed) {
+ *changed = 1;
+ }
+ return error;
+}
+
+int
+sysctl_io_string(struct sysctl_req *req, char *pValue, size_t valueSize, int trunc, int *changed)
+{
+ int error;
+ size_t len = strlen(pValue) + 1;
+
+ if (changed) {
+ *changed = 0;
+ }
+
+ if (trunc && req->oldptr && req->oldlen && (req->oldlen < len)) {
+ /* If trunc != 0, if you give it a too small (but larger than
+ * 0 bytes) buffer, instead of returning ENOMEM, it truncates the
+ * returned string to the buffer size. This preserves the semantics
+ * of some library routines implemented via sysctl, which truncate
+ * their returned data, rather than simply returning an error. The
+ * returned string is always nul (ascii '\0') terminated. */
+ error = SYSCTL_OUT(req, pValue, req->oldlen - 1);
+ if (!error) {
+ char c = '\0';
+ error = SYSCTL_OUT(req, &c, 1);
+ }
+ } else {
+ /* Copy string out */
+ error = SYSCTL_OUT(req, pValue, len);
+ }
+
+ /* error or no new value */
+ if (error || !req->newptr) {
+ return error;
+ }
+
+ /* attempt to set read-only value */
+ if (valueSize == 0) {
+ return EPERM;
+ }
+
+ /* make sure there's room for the new string */
+ if (req->newlen >= valueSize) {
+ return EINVAL;
+ }
+
+ /* copy the string in and force nul termination */
+ error = SYSCTL_IN(req, pValue, req->newlen);
+ pValue[req->newlen] = '\0';
+
+ if (!error && changed) {
+ *changed = 1;
+ }
+ return error;