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

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