diff options
| author | Alan Somers <asomers@FreeBSD.org> | 2021-07-21 21:11:00 +0000 |
|---|---|---|
| committer | Alan Somers <asomers@FreeBSD.org> | 2021-08-22 22:43:50 +0000 |
| commit | 98c467192082b3d4a6c91eeaa80868bb5231534c (patch) | |
| tree | c405061121f0555dea9aab3510b73ff61e8db8ce | |
| parent | 0417804260fd61ab47424632c26532d6ce2fed14 (diff) | |
| download | src-98c467192082b3d4a6c91eeaa80868bb5231534c.tar.gz src-98c467192082b3d4a6c91eeaa80868bb5231534c.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
Sponsored by: Axcient
Reviewed by: freqlabs
Differential Revision: https://reviews.freebsd.org/D31265
(cherry picked from commit 6c9506559080da2914749bf611225d7c0a153609)
| -rw-r--r-- | sys/kern/kern_sysctl.c | 47 |
1 files changed, 45 insertions, 2 deletions
diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c index fa0398896ad0..d40eec348ae3 100644 --- a/sys/kern/kern_sysctl.c +++ b/sys/kern/kern_sysctl.c @@ -109,6 +109,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); @@ -734,6 +735,45 @@ 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) @@ -816,14 +856,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 */ @@ -842,7 +885,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; |
