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

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