Blame SOURCES/0035-grubby-properly-handle-mixed-and-and-nested-quotes.patch

56d25d
From 280f1ed9a0066cb508d77fe6d072c7c5aa772967 Mon Sep 17 00:00:00 2001
56d25d
From: Nishanth Aravamudan <nacc@linux.vnet.ibm.com>
56d25d
Date: Tue, 16 Jun 2015 10:43:21 -0700
56d25d
Subject: [PATCH 35/41] grubby: properly handle mixed ' and " and nested quotes
56d25d
56d25d
The SLES12 grub2.cfg file on ppc64le by default contains a line like:
56d25d
56d25d
  submenu "Bootable snapshot #$snapshot_num" {
56d25d
    menuentry "If OK, run 'snapper rollback $snapshot_num' reboot." { true; }
56d25d
  }
56d25d
56d25d
On any grubby (tested with 8.40) invocation that updates the config
56d25d
file, the combination of nested quotes and mixed quotes leads to a
56d25d
generated file content like:
56d25d
56d25d
  submenu "Bootable snapshot #$snapshot_num" {
56d25d
    menuentry 'If OK, run snapper rollback $snapshot_num' rollback $snapshot_num' and reboot." { true; }
56d25d
  }
56d25d
56d25d
which includes both a change from " to ', but also improperly quoted
56d25d
strings and trailing characters relative to the string. This actually
56d25d
leads to a failure to boot from the disk by default when using grubby
56d25d
(e.g., Autotest) on SLES12 ppc64le. Whether SLES12 should be adding an
56d25d
entry like this by default or not is probably open to debate, but grubby
56d25d
should be able to hand this input file.
56d25d
56d25d
To fix the issue, three changes were necessary:
56d25d
56d25d
1) grub2ExtractTitle needs to check that if the second element starts
56d25d
with a quote, that the matching element found ends with the same
56d25d
quote-type (' vs. ")
56d25d
56d25d
2) lineWrite needs to output the right kind of quote based upon if the
56d25d
string to be outputted itself contains quotes. This is not currently
56d25d
possible in the code, because quotes are stripped out normally by
56d25d
readConfig, but with the change in 3), that only happens now for the
56d25d
quotes that actually delineate a string.
56d25d
56d25d
3) readConfig needs to check that when it is extracting titles and
56d25d
determining extras, it uses matching quotes.
56d25d
56d25d
With these changes, a simple grubby --set-default=SLES12 (for example),
56d25d
now produces:
56d25d
56d25d
   submenu "Bootable snapshot #$snapshot_num" {
56d25d
     menuentry "If OK, run 'snapper rollback $snapshot_num' and reboot." { true; }
56d25d
   }
56d25d
56d25d
as expected.
56d25d
56d25d
Signed-off-by: Nishanth Aravamudan <nacc@linux.vnet.ibm.com>
56d25d
---
56d25d
 grubby.c | 42 +++++++++++++++++++++++++++++++++---------
56d25d
 1 file changed, 33 insertions(+), 9 deletions(-)
56d25d
56d25d
diff --git a/grubby.c b/grubby.c
56d25d
index 232704a..407c65d 100644
56d25d
--- a/grubby.c
56d25d
+++ b/grubby.c
56d25d
@@ -446,6 +446,8 @@ char *grub2ExtractTitle(struct singleLine * line) {
56d25d
      * whose last character is also quote (assuming it's the closing one) */
56d25d
     int resultMaxSize;
56d25d
     char * result;
56d25d
+    /* need to ensure that ' does not match " as we search */
56d25d
+    char quote_char = *current;
56d25d
     
56d25d
     resultMaxSize = sizeOfSingleLine(line);
56d25d
     result = malloc(resultMaxSize);
56d25d
@@ -459,7 +461,7 @@ char *grub2ExtractTitle(struct singleLine * line) {
56d25d
 	current_indent_len = strlen(current_indent);
56d25d
 
56d25d
 	strncat(result, current_indent, current_indent_len);
56d25d
-	if (!isquote(current[current_len-1])) {
56d25d
+	if (current[current_len-1] != quote_char) {
56d25d
 	    strncat(result, current, current_len);
56d25d
 	} else {
56d25d
 	    strncat(result, current, current_len - 1);
56d25d
@@ -921,10 +923,23 @@ static int lineWrite(FILE * out, struct singleLine * line,
56d25d
 	/* Need to handle this, because we strip the quotes from
56d25d
 	 * menuentry when read it. */
56d25d
 	if (line->type == LT_MENUENTRY && i == 1) {
56d25d
-	    if(!isquote(*line->elements[i].item))
56d25d
-		fprintf(out, "\'%s\'", line->elements[i].item);
56d25d
-	    else
56d25d
+	    if(!isquote(*line->elements[i].item)) {
56d25d
+		int substring = 0;
56d25d
+		/* If the line contains nested quotes, we did not strip
56d25d
+		 * the "interna" quotes and we must use the right quotes
56d25d
+		 * again when writing the updated file. */
56d25d
+		for (int j = i; j < line->numElements; j++) {
56d25d
+		    if (strchr(line->elements[i].item, '\'') != NULL) {
56d25d
+		       substring = 1;
56d25d
+		       fprintf(out, "\"%s\"", line->elements[i].item);
56d25d
+		       break;
56d25d
+		    }
56d25d
+		}
56d25d
+		if (!substring)
56d25d
+		    fprintf(out, "\'%s\'", line->elements[i].item);
56d25d
+	    } else {
56d25d
 		fprintf(out, "%s", line->elements[i].item);
56d25d
+	    }
56d25d
 	    fprintf(out, "%s", line->elements[i].indent);
56d25d
 
56d25d
 	    continue;
56d25d
@@ -1260,6 +1275,8 @@ static struct grubConfig * readConfig(const char * inName,
56d25d
 	    len = 0;
56d25d
 	    char *extras;
56d25d
 	    char *title;
56d25d
+	    /* initially unseen value */
56d25d
+	    char quote_char = '\0';
56d25d
 
56d25d
 	    for (int i = 1; i < line->numElements; i++) {
56d25d
 		len += strlen(line->elements[i].item);
56d25d
@@ -1276,13 +1293,16 @@ static struct grubConfig * readConfig(const char * inName,
56d25d
 	    for (int i = 0; i < line->numElements; i++) {
56d25d
 		if (!strcmp(line->elements[i].item, "menuentry"))
56d25d
 		    continue;
56d25d
-		if (isquote(*line->elements[i].item))
56d25d
+		if (isquote(*line->elements[i].item) && quote_char == '\0') {
56d25d
+		    /* ensure we properly pair off quotes */
56d25d
+		    quote_char = *line->elements[i].item;
56d25d
 		    title = line->elements[i].item + 1;
56d25d
-		else
56d25d
+		} else {
56d25d
 		    title = line->elements[i].item;
56d25d
+		}
56d25d
 
56d25d
 		len = strlen(title);
56d25d
-	        if (isquote(title[len-1])) {
56d25d
+	        if (title[len-1] == quote_char) {
56d25d
 		    strncat(buf, title,len-1);
56d25d
 		    break;
56d25d
 		} else {
56d25d
@@ -1293,6 +1313,7 @@ static struct grubConfig * readConfig(const char * inName,
56d25d
 
56d25d
 	    /* get extras */
56d25d
 	    int count = 0;
56d25d
+	    quote_char = '\0';
56d25d
 	    for (int i = 0; i < line->numElements; i++) {
56d25d
 		if (count >= 2) {
56d25d
 		    strcat(extras, line->elements[i].item);
56d25d
@@ -1303,12 +1324,15 @@ static struct grubConfig * readConfig(const char * inName,
56d25d
 		    continue;
56d25d
 
56d25d
 		/* count ' or ", there should be two in menuentry line. */
56d25d
-		if (isquote(*line->elements[i].item))
56d25d
+		if (isquote(*line->elements[i].item) && quote_char == '\0') {
56d25d
+		    /* ensure we properly pair off quotes */
56d25d
+	            quote_char = *line->elements[i].item;
56d25d
 		    count++;
56d25d
+		}
56d25d
 
56d25d
 		len = strlen(line->elements[i].item);
56d25d
 
56d25d
-		if (isquote(line->elements[i].item[len -1]))
56d25d
+		if (line->elements[i].item[len -1] == quote_char)
56d25d
 		    count++;
56d25d
 
56d25d
 		/* ok, we get the final ' or ", others are extras. */
56d25d
-- 
56d25d
2.4.3
56d25d