[PATCH] getmntent: Generalize octal decoding

Siddhesh Poyarekar siddhesh@sourceware.org
Wed Dec 16 14:24:15 GMT 2020


The current octal decoding looks for specific characters to decode.
generalize this instead, allowing any arbitrary \xxx octal pattern to
be decoded as long as it is a printable character.

The decoding is now done a bit later so that it does not influence the
"ignore" option of autofs, which is a hint for getmntent to skip over
the line.  With this change, "\151gnore" will not cuase the line to be
skipped over, but the options string will show "ignore" correctly.

Also add a test case to validate that encoded hashes get decoded
correctly.  This would be useful if/when the kernel starts escaping
hashes in /proc/mounts.
---
 misc/mntent_r.c          | 92 ++++++++++++++++++++++++----------------
 misc/tst-mntent-autofs.c |  4 +-
 2 files changed, 58 insertions(+), 38 deletions(-)

diff --git a/misc/mntent_r.c b/misc/mntent_r.c
index b50ba0de89..ac56e40fd5 100644
--- a/misc/mntent_r.c
+++ b/misc/mntent_r.c
@@ -65,6 +65,27 @@ __endmntent (FILE *stream)
 libc_hidden_def (__endmntent)
 weak_alias (__endmntent, endmntent)
 
+static char
+parse_octal (char *s)
+{
+  char c = 0, t;
+
+  t = s[0] - '0';
+  if (t & (~0x7))
+    return 0;
+  c |= t << 6;
+
+  t = s[1] - '0';
+  if (t & (~0x7))
+    return 0;
+  c |= t << 3;
+
+  t = s[2] - '0';
+  if (t & (~0x7))
+    return 0;
+
+  return c | t;
+}
 
 /* Since the values in a line are separated by spaces, a name cannot
    contain a space.  Therefore some programs encode spaces in names
@@ -77,43 +98,42 @@ decode_name (char *buf)
   char *wp = buf;
 
   do
-    if (rp[0] == '\\' && rp[1] == '0' && rp[2] == '4' && rp[3] == '0')
-      {
-	/* \040 is a SPACE.  */
-	*wp++ = ' ';
-	rp += 3;
-      }
-    else if (rp[0] == '\\' && rp[1] == '0' && rp[2] == '1' && rp[3] == '1')
-      {
-	/* \011 is a TAB.  */
-	*wp++ = '\t';
-	rp += 3;
-      }
-    else if (rp[0] == '\\' && rp[1] == '0' && rp[2] == '1' && rp[3] == '2')
-      {
-	/* \012 is a NEWLINE.  */
-	*wp++ = '\n';
-	rp += 3;
-      }
-    else if (rp[0] == '\\' && rp[1] == '\\')
-      {
-	/* We have to escape \\ to be able to represent all characters.  */
-	*wp++ = '\\';
-	rp += 1;
-      }
-    else if (rp[0] == '\\' && rp[1] == '1' && rp[2] == '3' && rp[3] == '4')
-      {
-	/* \134 is also \\.  */
-	*wp++ = '\\';
-	rp += 3;
-      }
-    else
+    {
+      if (rp[0] == '\\')
+	{
+	  char c;
+
+	  if ((c = parse_octal (&rp[1])) != 0)
+	    {
+	      *wp++ = c;
+	      rp += 3;
+	      continue;
+	    }
+	  if (rp[1] == '\\')
+	    {
+	      *wp++ = '\\';
+	      rp++;
+	      continue;
+	    }
+	}
       *wp++ = *rp;
+    }
   while (*rp++ != '\0');
 
   return buf;
 }
 
+static struct mntent *
+decode_mntent_names (struct mntent *res)
+{
+  res->mnt_fsname = decode_name (res->mnt_fsname);
+  res->mnt_dir = decode_name (res->mnt_dir);
+  res->mnt_type = decode_name (res->mnt_type);
+  res->mnt_opts = decode_name (res->mnt_opts);
+
+  return res;
+}
+
 static bool
 get_mnt_entry (FILE *stream, struct mntent *mp, char *buffer, int bufsiz)
 {
@@ -151,19 +171,19 @@ get_mnt_entry (FILE *stream, struct mntent *mp, char *buffer, int bufsiz)
   while (head[0] == '\0' || head[0] == '#');
 
   cp = __strsep (&head, " \t");
-  mp->mnt_fsname = cp != NULL ? decode_name (cp) : (char *) "";
+  mp->mnt_fsname = cp != NULL ? cp : (char *) "";
   if (head)
     head += strspn (head, " \t");
   cp = __strsep (&head, " \t");
-  mp->mnt_dir = cp != NULL ? decode_name (cp) : (char *) "";
+  mp->mnt_dir = cp != NULL ? cp : (char *) "";
   if (head)
     head += strspn (head, " \t");
   cp = __strsep (&head, " \t");
-  mp->mnt_type = cp != NULL ? decode_name (cp) : (char *) "";
+  mp->mnt_type = cp != NULL ? cp : (char *) "";
   if (head)
     head += strspn (head, " \t");
   cp = __strsep (&head, " \t");
-  mp->mnt_opts = cp != NULL ? decode_name (cp) : (char *) "";
+  mp->mnt_opts = cp != NULL ? cp : (char *) "";
   switch (head ? sscanf (head, " %d %d ", &mp->mnt_freq, &mp->mnt_passno) : 0)
     {
     case 0:
@@ -197,7 +217,7 @@ __getmntent_r (FILE *stream, struct mntent *mp, char *buffer, int bufsiz)
 	  memset (mp, 0, sizeof (*mp));
 	else
 	  {
-	    result = mp;
+	    result = decode_mntent_names (mp);
 	    break;
 	  }
       }
diff --git a/misc/tst-mntent-autofs.c b/misc/tst-mntent-autofs.c
index e4c509f520..54b11b23ba 100644
--- a/misc/tst-mntent-autofs.c
+++ b/misc/tst-mntent-autofs.c
@@ -87,8 +87,8 @@ static struct test_case test_cases[] =
     /* These are not filtered because the string is escaped.  '\151'
        is 'i', but it is not actually decoded by the parser.  */
     { "/etc/auto.\\151gnore /mnt/auto/16 autofs \\151gnore 0 0",
-      { "/etc/auto.\\151gnore", "/mnt/auto/16", "autofs",
-        "\\151gnore", } },
+      { "/etc/auto.ignore", "/mnt/auto/16", "autofs",
+        "ignore", } },
   };
 
 static int
-- 
2.29.2



More information about the Libc-alpha mailing list