0f5ce5a
From 112b6e5fc690b2a73b6ad8c92dc4645db08503b6 Mon Sep 17 00:00:00 2001
8a91c9f
From: Nathaniel McCallum <npmccallum@redhat.com>
8a91c9f
Date: Fri, 2 Mar 2018 08:40:18 -0500
0f5ce5a
Subject: [PATCH 3/8] Add btrfs subvolume support for grub2
8a91c9f
8a91c9f
In order to find the subvolume prefix from a given path, we parse
8a91c9f
/proc/mounts. In cases where /proc/mounts doesn't contain the
8a91c9f
filesystem, the caller can use the --mounts option to specify his own
8a91c9f
mounts file.
8a91c9f
8a91c9f
Btrfs subvolumes are already supported by grub2 and by grub2-mkconfig.
8a91c9f
8a91c9f
Fixes #22
8a91c9f
---
0f5ce5a
 grubby.c | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
8a91c9f
 1 file changed, 143 insertions(+), 5 deletions(-)
8a91c9f
8a91c9f
diff --git a/grubby.c b/grubby.c
0f5ce5a
index a062ef8e567..96d252a0a83 100644
8a91c9f
--- a/grubby.c
8a91c9f
+++ b/grubby.c
8a91c9f
@@ -68,6 +68,8 @@ int isEfi = 0;
8a91c9f
 
8a91c9f
 char *saved_command_line = NULL;
8a91c9f
 
8a91c9f
+const char *mounts = "/proc/mounts";
8a91c9f
+
8a91c9f
 /* comments get lumped in with indention */
8a91c9f
 struct lineElement {
8a91c9f
     char * item;
8a91c9f
@@ -1834,6 +1836,129 @@ static int endswith(const char *s, char c)
8a91c9f
 	return s[slen] == c;
8a91c9f
 }
8a91c9f
 
8a91c9f
+typedef struct {
8a91c9f
+	const char *start;
8a91c9f
+	size_t      chars;
8a91c9f
+} field;
8a91c9f
+
8a91c9f
+static int iscomma(int c)
8a91c9f
+{
8a91c9f
+	return c == ',';
8a91c9f
+}
8a91c9f
+
8a91c9f
+static int isequal(int c)
8a91c9f
+{
8a91c9f
+	return c == '=';
8a91c9f
+}
8a91c9f
+
8a91c9f
+static field findField(const field *in, typeof(isspace) *isdelim, field *out)
8a91c9f
+{
8a91c9f
+	field nxt = {};
8a91c9f
+	size_t off = 0;
8a91c9f
+
8a91c9f
+	while (off < in->chars && isdelim(in->start[off]))
8a91c9f
+		off++;
8a91c9f
+
8a91c9f
+	if (off == in->chars)
8a91c9f
+		return nxt;
8a91c9f
+
8a91c9f
+	out->start = &in->start[off];
8a91c9f
+	out->chars = 0;
8a91c9f
+
8a91c9f
+	while (off + out->chars < in->chars && !isdelim(out->start[out->chars]))
8a91c9f
+		out->chars++;
8a91c9f
+
8a91c9f
+	nxt.start = out->start + out->chars;
8a91c9f
+	nxt.chars = in->chars - off - out->chars;
8a91c9f
+	return nxt;
8a91c9f
+}
8a91c9f
+
8a91c9f
+static int fieldEquals(const field *in, const char *str)
8a91c9f
+{
8a91c9f
+	return in->chars == strlen(str) &&
8a91c9f
+		strncmp(in->start, str, in->chars) == 0;
8a91c9f
+}
8a91c9f
+
8a91c9f
+/* Parse /proc/mounts to determine the subvolume prefix. */
8a91c9f
+static size_t subvolPrefix(const char *str)
8a91c9f
+{
8a91c9f
+	FILE *file = NULL;
8a91c9f
+	char *line = NULL;
8a91c9f
+	size_t prfx = 0;
8a91c9f
+	size_t size = 0;
8a91c9f
+
8a91c9f
+	file = fopen(mounts, "r");
8a91c9f
+	if (!file)
8a91c9f
+		return 0;
8a91c9f
+
8a91c9f
+	for (ssize_t s; (s = getline(&line, &size, file)) >= 0; ) {
8a91c9f
+		field nxt = { line, s };
8a91c9f
+		field dev = {};
8a91c9f
+		field path = {};
8a91c9f
+		field type = {};
8a91c9f
+		field opts = {};
8a91c9f
+		field opt = {};
8a91c9f
+
8a91c9f
+		nxt = findField(&nxt, isspace, &dev;;
8a91c9f
+		if (!nxt.start)
8a91c9f
+			continue;
8a91c9f
+
8a91c9f
+		nxt = findField(&nxt, isspace, &path);
8a91c9f
+		if (!nxt.start)
8a91c9f
+			continue;
8a91c9f
+
8a91c9f
+		nxt = findField(&nxt, isspace, &type);
8a91c9f
+		if (!nxt.start)
8a91c9f
+			continue;
8a91c9f
+
8a91c9f
+		nxt = findField(&nxt, isspace, &opts);
8a91c9f
+		if (!nxt.start)
8a91c9f
+			continue;
8a91c9f
+
8a91c9f
+		if (!fieldEquals(&type, "btrfs"))
8a91c9f
+			continue;
8a91c9f
+
8a91c9f
+		/* We have found a btrfs mount point. */
8a91c9f
+
8a91c9f
+		nxt = opts;
8a91c9f
+		while ((nxt = findField(&nxt, iscomma, &opt)).start) {
8a91c9f
+			field key = {};
8a91c9f
+			field val = {};
8a91c9f
+
8a91c9f
+			opt = findField(&opt, isequal, &key);
8a91c9f
+			if (!opt.start)
8a91c9f
+				continue;
8a91c9f
+
8a91c9f
+			opt = findField(&opt, isequal, &val;;
8a91c9f
+			if (!opt.start)
8a91c9f
+				continue;
8a91c9f
+
8a91c9f
+			if (!fieldEquals(&key, "subvol"))
8a91c9f
+				continue;
8a91c9f
+
8a91c9f
+			/* We have found a btrfs subvolume mount point. */
8a91c9f
+
8a91c9f
+			if (strncmp(val.start, str, val.chars))
8a91c9f
+				continue;
8a91c9f
+
8a91c9f
+			if (val.start[val.chars - 1] != '/' &&
8a91c9f
+				str[val.chars] != '/')
8a91c9f
+				continue;
8a91c9f
+
8a91c9f
+			/* The subvolume mount point matches our input. */
8a91c9f
+
8a91c9f
+			if (prfx < val.chars)
8a91c9f
+				prfx = val.chars;
8a91c9f
+		}
8a91c9f
+	}
8a91c9f
+
8a91c9f
+	dbgPrintf("%s(): str: '%s', prfx: '%s'\n", __FUNCTION__, str, prfx);
8a91c9f
+
8a91c9f
+	fclose(file);
8a91c9f
+	free(line);
8a91c9f
+	return prfx;
8a91c9f
+}
8a91c9f
+
8a91c9f
 int suitableImage(struct singleEntry * entry, const char * bootPrefix,
8a91c9f
 		  int skipRemoved, int flags) {
8a91c9f
     struct singleLine * line;
8a91c9f
@@ -2794,12 +2919,22 @@ struct singleLine * addLineTmpl(struct singleEntry * entry,
8a91c9f
 
8a91c9f
 	/* but try to keep the rootspec from the template... sigh */
8a91c9f
 	if (tmplLine->type & (LT_HYPER|LT_KERNEL|LT_MBMODULE|LT_INITRD|LT_KERNEL_EFI|LT_INITRD_EFI|LT_KERNEL_16|LT_INITRD_16)) {
8a91c9f
-            size_t rs = getRootSpecifier(tmplLine->elements[1].item);
8a91c9f
+            const char *prfx = tmplLine->elements[1].item;
8a91c9f
+            size_t rs = getRootSpecifier(prfx);
8a91c9f
+            if (isinitrd(tmplLine->type)) {
8a91c9f
+                for (struct singleLine *l = entry->lines;
8a91c9f
+                     rs == 0 && l; l = l->next) {
8a91c9f
+                    if (iskernel(l->type)) {
8a91c9f
+                        prfx = l->elements[1].item;
8a91c9f
+                        rs = getRootSpecifier(prfx);
8a91c9f
+                    }
8a91c9f
+                }
8a91c9f
+            }
8a91c9f
             if (rs > 0) {
8a91c9f
                 free(newLine->elements[1].item);
8a91c9f
-                newLine->elements[1].item = sdupprintf("%.*s%s", (int) rs,
8a91c9f
-                    tmplLine->elements[1].item, val);
8a91c9f
-	    }
8a91c9f
+                newLine->elements[1].item = sdupprintf("%.*s%s",
8a91c9f
+						       (int) rs, prfx, val);
8a91c9f
+            }
8a91c9f
 	}
8a91c9f
     }
8a91c9f
 
8a91c9f
@@ -3738,7 +3873,7 @@ static size_t getRootSpecifier(const char *str)
8a91c9f
         rs++;
8a91c9f
     }
8a91c9f
 
8a91c9f
-    return rs;
8a91c9f
+	return rs + subvolPrefix(str + rs);
8a91c9f
 }
8a91c9f
 
8a91c9f
 static char * getInitrdVal(struct grubConfig * config,
8a91c9f
@@ -4253,6 +4388,9 @@ int main(int argc, const char ** argv) {
8a91c9f
 	{ "mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0, 
8a91c9f
 	    _("default arguments for the new multiboot kernel or "
8a91c9f
               "new arguments for multiboot kernel being updated"), NULL },
8a91c9f
+	{ "mounts", 0, POPT_ARG_STRING, &mounts, 0,
8a91c9f
+            _("path to fake /proc/mounts file (for testing only)"),
8a91c9f
+            _("mounts") },
8a91c9f
 	{ "bad-image-okay", 0, 0, &badImageOkay, 0,
8a91c9f
 	    _("don't sanity check images in boot entries (for testing only)"), 
8a91c9f
 	    NULL },
8a91c9f
-- 
0f5ce5a
2.17.1
8a91c9f