diff options
author | Alan Somers <asomers@FreeBSD.org> | 2021-07-21 21:11:00 +0000 |
---|---|---|
committer | Alan Somers <asomers@FreeBSD.org> | 2021-07-22 16:22:48 +0000 |
commit | 6c9506559080da2914749bf611225d7c0a153609 (patch) | |
tree | 912fc5427150f0fe8988e3e8a9d5c4ba80604a54 | |
parent | a52936f1d5d533dec2e849c1ca001f5ec55d8106 (diff) | |
download | src-6c9506559080da2914749bf611225d7c0a153609.tar.gz src-6c9506559080da2914749bf611225d7c0a153609.zip |
Escape any '.' characters in sysctl node names
ZFS creates some sysctl nodes that include a pool name, and '.' is an
allowed character in pool names. But it's the separator in the sysctl
tree, so it can't be included in a sysctl name. Replace it with "%25".
Handily, "%" is illegal in ZFS pool names, so there's no ambiguity
there.
PR: 257316
MFC after: 3 weeks
Sponsored by: Axcient
Reviewed by: freqlabs
Differential Revision: https://reviews.freebsd.org/D31265
-rw-r--r-- | sys/kern/kern_sysctl.c | 48 |
1 files changed, 46 insertions, 2 deletions
diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c index e46584758c9b..c472db18aac7 100644 --- a/sys/kern/kern_sysctl.c +++ b/sys/kern/kern_sysctl.c @@ -122,6 +122,7 @@ static int sysctl_root(SYSCTL_HANDLER_ARGS); /* Root list */ struct sysctl_oid_list sysctl__children = SLIST_HEAD_INITIALIZER(&sysctl__children); +static char* sysctl_escape_name(const char*); static int sysctl_remove_oid_locked(struct sysctl_oid *oidp, int del, int recurse); static int sysctl_old_kernel(struct sysctl_req *, const void *, size_t); @@ -747,6 +748,46 @@ sysctl_remove_name(struct sysctl_oid *parent, const char *name, return (error); } +/* + * Duplicate the provided string, escaping any illegal characters. The result + * must be freed when no longer in use. + * + * The list of illegal characters is ".". + */ +static char* +sysctl_escape_name(const char* orig) +{ + int i, s = 0, d = 0, nillegals = 0; + char *new; + + /* First count the number of illegal characters */ + for (i = 0; orig[i] != '\0'; i++) { + if (orig[i] == '.') + nillegals++; + } + + /* Allocate storage for new string */ + new = malloc(i + 2 * nillegals + 1, M_SYSCTLOID, M_WAITOK); + + /* Copy the name, escaping characters as we go */ + while (orig[s] != '\0') { + if (orig[s] == '.') { + /* %25 is the hexadecimal representation of '.' */ + new[d++] = '%'; + new[d++] = '2'; + new[d++] = '5'; + s++; + } else { + new[d++] = orig[s++]; + } + } + + /* Finally, nul-terminate */ + new[d] = '\0'; + + return (new); +} + static int sysctl_remove_oid_locked(struct sysctl_oid *oidp, int del, int recurse) { @@ -828,14 +869,17 @@ sysctl_add_oid(struct sysctl_ctx_list *clist, struct sysctl_oid_list *parent, const char *label) { struct sysctl_oid *oidp; + char *escaped; /* You have to hook up somewhere.. */ if (parent == NULL) return(NULL); + escaped = sysctl_escape_name(name); /* Check if the node already exists, otherwise create it */ SYSCTL_WLOCK(); - oidp = sysctl_find_oidname(name, parent); + oidp = sysctl_find_oidname(escaped, parent); if (oidp != NULL) { + free(escaped, M_SYSCTLOID); if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE) { oidp->oid_refcnt++; /* Update the context */ @@ -854,7 +898,7 @@ sysctl_add_oid(struct sysctl_ctx_list *clist, struct sysctl_oid_list *parent, SLIST_INIT(&oidp->oid_children); oidp->oid_number = number; oidp->oid_refcnt = 1; - oidp->oid_name = strdup(name, M_SYSCTLOID); + oidp->oid_name = escaped; oidp->oid_handler = handler; oidp->oid_kind = CTLFLAG_DYN | kind; oidp->oid_arg1 = arg1; |