Add support for /boot on btrfs

This commit is contained in:
Nathaniel McCallum 2018-03-05 13:52:23 -05:00 committed by Peter Jones
parent e0fe64ca89
commit 7d47e62c2e
4 changed files with 2230 additions and 1 deletions

View File

@ -0,0 +1,143 @@
From c1c46d21182974181f5b4c2ed0a02288b4bfd880 Mon Sep 17 00:00:00 2001
From: Nathaniel McCallum <npmccallum@redhat.com>
Date: Fri, 2 Mar 2018 14:59:32 -0500
Subject: [PATCH 1/3] Change return type in getRootSpecifier()
Rather than returning a new allocation of the prefix, just return the
length of the prefix. This change accomplishes a couple things. First,
it reduces some memory leaks since the return value was often never
freed. Second, it simplifies the caller who is usually only interested
in the length of the prefix.
---
grubby.c | 54 +++++++++++++++++++++++++++---------------------------
1 file changed, 27 insertions(+), 27 deletions(-)
diff --git a/grubby.c b/grubby.c
index d4ebb86..a062ef8 100644
--- a/grubby.c
+++ b/grubby.c
@@ -675,7 +675,7 @@ static int lineWrite(FILE * out, struct singleLine * line,
struct configFileInfo * cfi);
static int getNextLine(char ** bufPtr, struct singleLine * line,
struct configFileInfo * cfi);
-static char * getRootSpecifier(char * str);
+static size_t getRootSpecifier(const char *str);
static void requote(struct singleLine *line, struct configFileInfo * cfi);
static void insertElement(struct singleLine * line,
const char * item, int insertHere,
@@ -1840,7 +1840,7 @@ int suitableImage(struct singleEntry * entry, const char * bootPrefix,
char * fullName;
int i;
char * dev;
- char * rootspec;
+ size_t rs;
char * rootdev;
if (skipRemoved && entry->skip) {
@@ -1866,12 +1866,11 @@ int suitableImage(struct singleEntry * entry, const char * bootPrefix,
fullName = alloca(strlen(bootPrefix) +
strlen(line->elements[1].item) + 1);
- rootspec = getRootSpecifier(line->elements[1].item);
- int rootspec_offset = rootspec ? strlen(rootspec) : 0;
+ rs = getRootSpecifier(line->elements[1].item);
int hasslash = endswith(bootPrefix, '/') ||
- beginswith(line->elements[1].item + rootspec_offset, '/');
+ beginswith(line->elements[1].item + rs, '/');
sprintf(fullName, "%s%s%s", bootPrefix, hasslash ? "" : "/",
- line->elements[1].item + rootspec_offset);
+ line->elements[1].item + rs);
if (access(fullName, R_OK)) {
notSuitablePrintf(entry, 0, "access to %s failed\n", fullName);
return 0;
@@ -1952,7 +1951,6 @@ struct singleEntry * findEntryByPath(struct grubConfig * config,
struct singleLine * line;
int i;
char * chptr;
- char * rootspec = NULL;
enum lineType_e checkType = LT_KERNEL;
if (isdigit(*kernel)) {
@@ -2044,11 +2042,10 @@ struct singleEntry * findEntryByPath(struct grubConfig * config,
if (line && line->type != LT_MENUENTRY &&
line->numElements >= 2) {
- rootspec = getRootSpecifier(line->elements[1].item);
- if (!strcmp(line->elements[1].item +
- ((rootspec != NULL) ? strlen(rootspec) : 0),
- kernel + strlen(prefix)))
- break;
+ if (!strcmp(line->elements[1].item +
+ getRootSpecifier(line->elements[1].item),
+ kernel + strlen(prefix)))
+ break;
}
if(line->type == LT_MENUENTRY &&
!strcmp(line->elements[1].item, kernel))
@@ -2797,11 +2794,11 @@ struct singleLine * addLineTmpl(struct singleEntry * entry,
/* but try to keep the rootspec from the template... sigh */
if (tmplLine->type & (LT_HYPER|LT_KERNEL|LT_MBMODULE|LT_INITRD|LT_KERNEL_EFI|LT_INITRD_EFI|LT_KERNEL_16|LT_INITRD_16)) {
- char * rootspec = getRootSpecifier(tmplLine->elements[1].item);
- if (rootspec != NULL) {
- free(newLine->elements[1].item);
- newLine->elements[1].item =
- sdupprintf("%s%s", rootspec, val);
+ size_t rs = getRootSpecifier(tmplLine->elements[1].item);
+ if (rs > 0) {
+ free(newLine->elements[1].item);
+ newLine->elements[1].item = sdupprintf("%.*s%s", (int) rs,
+ tmplLine->elements[1].item, val);
}
}
}
@@ -3729,15 +3726,19 @@ int checkForElilo(struct grubConfig * config) {
return 1;
}
-static char * getRootSpecifier(char * str) {
- char * idx, * rootspec = NULL;
+static size_t getRootSpecifier(const char *str)
+{
+ size_t rs = 0;
if (*str == '(') {
- idx = rootspec = strdup(str);
- while(*idx && (*idx != ')') && (!isspace(*idx))) idx++;
- *(++idx) = '\0';
+ for (; str[rs] != ')' && !isspace(str[rs]); rs++) {
+ if (!str[rs])
+ return rs;
+ }
+ rs++;
}
- return rootspec;
+
+ return rs;
}
static char * getInitrdVal(struct grubConfig * config,
@@ -4616,7 +4617,7 @@ int main(int argc, const char ** argv) {
if (displayDefault) {
struct singleLine * line;
struct singleEntry * entry;
- char * rootspec;
+ size_t rs;
if (config->defaultImage == -1) return 0;
if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
@@ -4629,9 +4630,8 @@ int main(int argc, const char ** argv) {
line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
if (!line) return 0;
- rootspec = getRootSpecifier(line->elements[1].item);
- printf("%s%s\n", bootPrefix, line->elements[1].item +
- ((rootspec != NULL) ? strlen(rootspec) : 0));
+ rs = getRootSpecifier(line->elements[1].item);
+ printf("%s%s\n", bootPrefix, line->elements[1].item + rs);
return 0;
--
2.14.3

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
Name: grubby
Version: 8.40
Release: 9%{?dist}
Release: 10%{?dist}
Summary: Command line tool for updating bootloader configs
License: GPLv2+
URL: https://github.com/rhinstaller/grubby
@ -10,6 +10,9 @@ URL: https://github.com/rhinstaller/grubby
# Source0: %%{name}-%%{version}.tar.bz2
Source0: https://github.com/rhboot/grubby/archive/%{version}-1.tar.gz
Patch1: drop-uboot-uImage-creation.patch
Patch2: 0001-Change-return-type-in-getRootSpecifier.patch
Patch3: 0002-Add-btrfs-subvolume-support-for-grub2.patch
Patch4: 0003-Add-tests-for-btrfs-support.patch
BuildRequires: pkgconfig glib2-devel popt-devel
BuildRequires: libblkid-devel git-core
@ -62,6 +65,9 @@ make install DESTDIR=$RPM_BUILD_ROOT mandir=%{_mandir}
%{_mandir}/man8/*.8*
%changelog
* Sat Mar 03 2018 Nathaniel McCallum <npmccallum@redhat.com> - 8.40-10
- Add support for /boot on btrfs subvolumes
* Wed Feb 07 2018 Fedora Release Engineering <releng@fedoraproject.org> - 8.40-9
- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild