git: 7208b3b845a6 - main - libsa: Add a generic URI-syntax devspec parser

From: Warner Losh <imp_at_FreeBSD.org>
Date: Fri, 11 Sep 2026 05:04:23 UTC
The branch main has been updated by imp:

URL: https://cgit.FreeBSD.org/src/commit/?id=7208b3b845a6fca40099a4eab0d86afc2fb84aa7

commit 7208b3b845a6fca40099a4eab0d86afc2fb84aa7
Author:     Warner Losh <imp@FreeBSD.org>
AuthorDate: 2026-09-11 04:13:37 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2026-09-11 04:15:28 +0000

    libsa: Add a generic URI-syntax devspec parser
    
    Add parse_uri(), a helper for devices whose devspec can take a
    "[N]://host[:port][/path]" form in addition to the traditional "N:path"
    form. It returns EINVAL when devspec doesn't match that pattern, so a
    caller's own dv_parsedev can fall back to the traditional form.
    
    Un-static default_parsedev() so it can serve as that fallback.
    
    Sponsored by:           Netflix
    Differential Revision:  https://reviews.freebsd.org/D59422
---
 stand/libsa/dev.c   | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 stand/libsa/stand.h |  2 ++
 2 files changed, 66 insertions(+), 1 deletion(-)

diff --git a/stand/libsa/dev.c b/stand/libsa/dev.c
index 1edc843d508c..fdaa9a84bcc1 100644
--- a/stand/libsa/dev.c
+++ b/stand/libsa/dev.c
@@ -64,7 +64,7 @@ devformat(struct devdesc *d)
 }
 
 /* NB: devspec points to the remainder of the device name after dv_name */
-static int
+int
 default_parsedev(struct devdesc **dev, const char *devspec,
     const char **path)
 {
@@ -102,6 +102,69 @@ fail:
 	return (err);
 }
 
+/*
+ * Helper for URI-syntax devices. NB: like default_parsedev(), devspec is
+ * the remainder after dv_name. Parses "[N]://host[:port][/path]"; EINVAL
+ * if devspec doesn't match that pattern.
+ */
+int
+parse_uri(const char *devspec, int *unitp, char **hostp, int *portp,
+    const char **pathp)
+{
+	const char *cp, *p;
+	char *end;
+	int unit;
+
+	cp = devspec;
+	unit = 0;
+	if (*cp != '\0' && *cp != ':') {
+		errno = 0;
+		unit = strtol(cp, &end, 0);
+		if (errno != 0 || end == cp)
+			return (EINVAL);
+		cp = end;
+	}
+	if (strncmp(cp, "://", 3) != 0)
+		return (EINVAL);
+	cp += 3;
+
+	p = cp;
+	while (*p != '\0' && *p != '/' && *p != ':')
+		p++;
+	if (p == cp)
+		return (EINVAL);		/* empty host */
+
+	*hostp = malloc(p - cp + 1);
+	if (*hostp == NULL)
+		return (ENOMEM);
+	memcpy(*hostp, cp, p - cp);
+	(*hostp)[p - cp] = '\0';
+
+	*portp = 0;
+	if (*p == ':') {
+		p++;
+		errno = 0;
+		*portp = strtol(p, &end, 10);
+		if (errno != 0 || end == p) {
+			free(*hostp);
+			*hostp = NULL;
+			return (EINVAL);
+		}
+		p = end;
+	}
+
+	if (*p != '\0' && *p != '/') {
+		free(*hostp);
+		*hostp = NULL;
+		return (EINVAL);
+	}
+
+	*unitp = unit;
+	if (pathp != NULL)
+		*pathp = p;
+	return (0);
+}
+
 /* NB: devspec points to the whole device spec, and possible trailing path */
 int
 devparse(struct devdesc **dev, const char *devspec, const char **path)
diff --git a/stand/libsa/stand.h b/stand/libsa/stand.h
index 6e363b145595..b024247fcafb 100644
--- a/stand/libsa/stand.h
+++ b/stand/libsa/stand.h
@@ -196,6 +196,8 @@ struct devdesc {
 
 char *devformat(struct devdesc *d);
 int devparse(struct devdesc **, const char *, const char **);
+int default_parsedev(struct devdesc **, const char *, const char **);
+int parse_uri(const char *, int *, char **, int *, const char **);
 int devinit(void);
 void	dev_cleanup(void);