All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] uml: Fix which_tmpdir failure when /dev/shm is a symlink, and in other edge cases
@ 2013-06-19 19:30 Tristan Schmelcher
  2013-06-19 19:59 ` Tristan Schmelcher
  0 siblings, 1 reply; 2+ messages in thread
From: Tristan Schmelcher @ 2013-06-19 19:30 UTC (permalink / raw)
  To: richard, jdike; +Cc: user-mode-linux-devel, linux-kernel

From: Tristan Schmelcher <tschmelcher@google.com>

which_tmpdir did the wrong thing if /dev/shm was a symlink (e.g., to /run/shm), if there were multiple mounts on top of each other, if the mount(s) were obscured by a later mount, or if /dev/shm was a prefix of another mount point. This fixes these cases. Applies to 3.9.6.

Signed-off-by: Tristan Schmelcher <tschmelcher@google.com>
---

--- linux-3.9.6/arch/um/os-Linux/mem.c.orig	2013-06-19 14:50:14.000000000 -0400
+++ linux-3.9.6/arch/um/os-Linux/mem.c	2013-06-19 14:53:07.000000000 -0400
@@ -53,6 +53,25 @@ static void __init find_tempdir(void)
  }
  
  /*
+ * Remove bytes from the front of the buffer and refill it so that if there's a
+ * partial string that we care about, it will be completed, and we can recognize
+ * it.
+ */
+static int pop(int fd, char *buf, size_t size, size_t npop)
+{
+	ssize_t n;
+	size_t len = strlen(&buf[npop]);
+
+	memmove(buf, &buf[npop], len + 1);
+	n = read(fd, &buf[len], size - len - 1);
+	if (n < 0)
+		return -errno;
+
+	buf[len + n] = '\0';
+	return 1;
+}
+
+/*
   * This will return 1, with the first character in buf being the
   * character following the next instance of c in the file.  This will
   * read the file as needed.  If there's an error, -errno is returned;
@@ -61,7 +80,6 @@ static void __init find_tempdir(void)
  static int next(int fd, char *buf, size_t size, char c)
  {
  	ssize_t n;
-	size_t len;
  	char *ptr;
  
  	while ((ptr = strchr(buf, c)) == NULL) {
@@ -74,20 +92,113 @@ static int next(int fd, char *buf, size_
  		buf[n] = '\0';
  	}
  
-	ptr++;
-	len = strlen(ptr);
-	memmove(buf, ptr, len + 1);
+	return pop(fd, buf, size, ptr - buf + 1);
+}
  
-	/*
-	 * Refill the buffer so that if there's a partial string that we care
-	 * about, it will be completed, and we can recognize it.
-	 */
-	n = read(fd, &buf[len], size - len - 1);
-	if (n < 0)
-		return -errno;
+/*
+ * Decode an octal-escaped and space-terminated path of the form used by
+ * /proc/mounts. May be used to decode a path in-place. "out" must be at least
+ * as large as the input.
+ */
+static int decode_path(const char *in, char *out, size_t *ndecoded,
+		       size_t *decodedlen)
+{
+	const char *first_in = in;
+	char *first_out = out;
+	int c;
+	int i;
+	while (*in) {
+		switch (*in) {
+		case ' ':
+			*out = '\0';
+			*ndecoded = in - first_in + 1;
+			*decodedlen = out - first_out;
+			return 0;
  
-	buf[len + n] = '\0';
-	return 1;
+		case '\\':
+			in++;
+			c = 0;
+			for (i = 0; i < 3; i++) {
+				if (*in < '0' || *in > '7')
+					return -EINVAL;
+				c = (c << 3) | (*in++ - '0');
+			}
+			*out++ = (char) c;
+			break;
+
+		default:
+			*out++ = *in++;
+			break;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static size_t octal_encoded_length(const char *s, const char *chars)
+{
+	size_t len = strlen(s);
+	while ((s = strpbrk(s, chars)) != NULL) {
+		len += 3;
+		s++;
+	}
+
+	return len;
+}
+
+enum {
+	OUTCOME_NOT_A_MOUNT_POINT,
+	OUTCOME_TMPFS_MOUNT,
+	OUTCOME_NON_TMPFS_MOUNT,
+};
+
+/* Read a line of /proc/mounts data looking for a tmpfs mount at "path". */
+static int read_mount(int fd, char *buf, size_t bufsize, const char *path,
+		      int *outcome)
+{
+	int found;
+	int match;
+	size_t ndecoded;
+	size_t decodedlen;
+
+	enum {
+		MATCH_NONE,
+		MATCH_EXACT,
+		MATCH_PARENT,
+	};
+
+	found = next(fd, buf, bufsize, ' ');
+	if (found != 1)
+		return found;
+
+	if (!decode_path(buf, buf, &ndecoded, &decodedlen)) {
+		match = MATCH_NONE;
+		if (!strcmp(buf, path))
+			match = MATCH_EXACT;
+		else if (!strncmp(buf, path, decodedlen)
+			 && (path[decodedlen] == '/' || !strcmp(buf, "/")))
+			match = MATCH_PARENT;
+
+		found = pop(fd, buf, bufsize, ndecoded);
+		if (found != 1)
+			return found;
+
+		switch (match) {
+		case MATCH_EXACT:
+			if (!strncmp(buf, "tmpfs", strlen("tmpfs")))
+				*outcome = OUTCOME_TMPFS_MOUNT;
+			else
+				*outcome = OUTCOME_NON_TMPFS_MOUNT;
+			break;
+
+		case MATCH_PARENT:
+			/* This mount obscures any previous ones. */
+			*outcome = OUTCOME_NOT_A_MOUNT_POINT;
+			break;
+		}
+	}
+
+	return next(fd, buf, bufsize, '\n');
  }
  
  /* which_tmpdir is called only during early boot */
@@ -106,8 +217,12 @@ static int checked_tmpdir = 0;
   */
  static void which_tmpdir(void)
  {
-	int fd, found;
-	char buf[128] = { '\0' };
+	int fd;
+	int found;
+	int outcome;
+	char *path;
+	char *buf;
+	size_t bufsize;
  
  	if (checked_tmpdir)
  		return;
@@ -116,49 +231,66 @@ static void which_tmpdir(void)
  
  	printf("Checking for tmpfs mount on /dev/shm...");
  
+	path = realpath("/dev/shm", NULL);
+	if (!path) {
+		printf("failed to check real path, errno = %d\n", errno);
+		return;
+	}
+	printf("%s...", path);
+
+	/*
+	 * The buffer needs to be able to fit the full octal-escaped path, a
+	 * space, and a trailing null in order to successfully decode it.
+	 */
+	bufsize = octal_encoded_length(path, " \t\n\\") + 2;
+
+	if (bufsize < 128)
+		bufsize = 128;
+
+	buf = malloc(bufsize);
+	if (!buf) {
+		printf("malloc failed, errno = %d\n", errno);
+		goto out;
+	}
+	buf[0] = '\0';
+
  	fd = open("/proc/mounts", O_RDONLY);
  	if (fd < 0) {
  		printf("failed to open /proc/mounts, errno = %d\n", errno);
-		return;
+		goto out1;
  	}
  
+	outcome = OUTCOME_NOT_A_MOUNT_POINT;
  	while (1) {
-		found = next(fd, buf, ARRAY_SIZE(buf), ' ');
-		if (found != 1)
-			break;
-
-		if (!strncmp(buf, "/dev/shm", strlen("/dev/shm")))
-			goto found;
-
-		found = next(fd, buf, ARRAY_SIZE(buf), '\n');
+		found = read_mount(fd, buf, bufsize, path, &outcome);
  		if (found != 1)
  			break;
  	}
  
-err:
-	if (found == 0)
-		printf("nothing mounted on /dev/shm\n");
-	else if (found < 0)
+	if (found < 0) {
  		printf("read returned errno %d\n", -found);
+	} else {
+		switch (outcome) {
+		case OUTCOME_TMPFS_MOUNT:
+			printf("OK\n");
+			default_tmpdir = "/dev/shm";
+			break;
  
-out:
-	close(fd);
-
-	return;
-
-found:
-	found = next(fd, buf, ARRAY_SIZE(buf), ' ');
-	if (found != 1)
-		goto err;
+		case OUTCOME_NON_TMPFS_MOUNT:
+			printf("not tmpfs\n");
+			break;
  
-	if (strncmp(buf, "tmpfs", strlen("tmpfs"))) {
-		printf("not tmpfs\n");
-		goto out;
+		default:
+			printf("nothing mounted on /dev/shm\n");
+			break;
+		}
  	}
  
-	printf("OK\n");
-	default_tmpdir = "/dev/shm";
-	goto out;
+	close(fd);
+out1:
+	free(buf);
+out:
+	free(path);
  }
  
  static int __init make_tempfile(const char *template, char **out_tempname,


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH] uml: Fix which_tmpdir failure when /dev/shm is a symlink, and in other edge cases
  2013-06-19 19:30 [PATCH] uml: Fix which_tmpdir failure when /dev/shm is a symlink, and in other edge cases Tristan Schmelcher
@ 2013-06-19 19:59 ` Tristan Schmelcher
  0 siblings, 0 replies; 2+ messages in thread
From: Tristan Schmelcher @ 2013-06-19 19:59 UTC (permalink / raw)
  To: Richard Weinberger, jdike; +Cc: user-mode-linux-devel, linux-kernel

And of course I found a bug in it moments after sending. Please
ignore, v2 coming shortly.

On 19 June 2013 15:30, Tristan Schmelcher <tschmelcher@google.com> wrote:
> From: Tristan Schmelcher <tschmelcher@google.com>
>
> which_tmpdir did the wrong thing if /dev/shm was a symlink (e.g., to
> /run/shm), if there were multiple mounts on top of each other, if the
> mount(s) were obscured by a later mount, or if /dev/shm was a prefix of
> another mount point. This fixes these cases. Applies to 3.9.6.
>
> Signed-off-by: Tristan Schmelcher <tschmelcher@google.com>
> ---
>
> --- linux-3.9.6/arch/um/os-Linux/mem.c.orig     2013-06-19
> 14:50:14.000000000 -0400
> +++ linux-3.9.6/arch/um/os-Linux/mem.c  2013-06-19 14:53:07.000000000 -0400
> @@ -53,6 +53,25 @@ static void __init find_tempdir(void)
>  }
>   /*
> + * Remove bytes from the front of the buffer and refill it so that if
> there's a
> + * partial string that we care about, it will be completed, and we can
> recognize
> + * it.
> + */
> +static int pop(int fd, char *buf, size_t size, size_t npop)
> +{
> +       ssize_t n;
> +       size_t len = strlen(&buf[npop]);
> +
> +       memmove(buf, &buf[npop], len + 1);
> +       n = read(fd, &buf[len], size - len - 1);
> +       if (n < 0)
> +               return -errno;
> +
> +       buf[len + n] = '\0';
> +       return 1;
> +}
> +
> +/*
>   * This will return 1, with the first character in buf being the
>   * character following the next instance of c in the file.  This will
>   * read the file as needed.  If there's an error, -errno is returned;
> @@ -61,7 +80,6 @@ static void __init find_tempdir(void)
>  static int next(int fd, char *buf, size_t size, char c)
>  {
>         ssize_t n;
> -       size_t len;
>         char *ptr;
>         while ((ptr = strchr(buf, c)) == NULL) {
> @@ -74,20 +92,113 @@ static int next(int fd, char *buf, size_
>                 buf[n] = '\0';
>         }
>  -      ptr++;
> -       len = strlen(ptr);
> -       memmove(buf, ptr, len + 1);
> +       return pop(fd, buf, size, ptr - buf + 1);
> +}
>  -      /*
> -        * Refill the buffer so that if there's a partial string that we
> care
> -        * about, it will be completed, and we can recognize it.
> -        */
> -       n = read(fd, &buf[len], size - len - 1);
> -       if (n < 0)
> -               return -errno;
> +/*
> + * Decode an octal-escaped and space-terminated path of the form used by
> + * /proc/mounts. May be used to decode a path in-place. "out" must be at
> least
> + * as large as the input.
> + */
> +static int decode_path(const char *in, char *out, size_t *ndecoded,
> +                      size_t *decodedlen)
> +{
> +       const char *first_in = in;
> +       char *first_out = out;
> +       int c;
> +       int i;
> +       while (*in) {
> +               switch (*in) {
> +               case ' ':
> +                       *out = '\0';
> +                       *ndecoded = in - first_in + 1;
> +                       *decodedlen = out - first_out;
> +                       return 0;
>  -      buf[len + n] = '\0';
> -       return 1;
> +               case '\\':
> +                       in++;
> +                       c = 0;
> +                       for (i = 0; i < 3; i++) {
> +                               if (*in < '0' || *in > '7')
> +                                       return -EINVAL;
> +                               c = (c << 3) | (*in++ - '0');
> +                       }
> +                       *out++ = (char) c;
> +                       break;
> +
> +               default:
> +                       *out++ = *in++;
> +                       break;
> +               }
> +       }
> +
> +       return -EINVAL;
> +}
> +
> +static size_t octal_encoded_length(const char *s, const char *chars)
> +{
> +       size_t len = strlen(s);
> +       while ((s = strpbrk(s, chars)) != NULL) {
> +               len += 3;
> +               s++;
> +       }
> +
> +       return len;
> +}
> +
> +enum {
> +       OUTCOME_NOT_A_MOUNT_POINT,
> +       OUTCOME_TMPFS_MOUNT,
> +       OUTCOME_NON_TMPFS_MOUNT,
> +};
> +
> +/* Read a line of /proc/mounts data looking for a tmpfs mount at "path". */
> +static int read_mount(int fd, char *buf, size_t bufsize, const char *path,
> +                     int *outcome)
> +{
> +       int found;
> +       int match;
> +       size_t ndecoded;
> +       size_t decodedlen;
> +
> +       enum {
> +               MATCH_NONE,
> +               MATCH_EXACT,
> +               MATCH_PARENT,
> +       };
> +
> +       found = next(fd, buf, bufsize, ' ');
> +       if (found != 1)
> +               return found;
> +
> +       if (!decode_path(buf, buf, &ndecoded, &decodedlen)) {
> +               match = MATCH_NONE;
> +               if (!strcmp(buf, path))
> +                       match = MATCH_EXACT;
> +               else if (!strncmp(buf, path, decodedlen)
> +                        && (path[decodedlen] == '/' || !strcmp(buf, "/")))
> +                       match = MATCH_PARENT;
> +
> +               found = pop(fd, buf, bufsize, ndecoded);
> +               if (found != 1)
> +                       return found;
> +
> +               switch (match) {
> +               case MATCH_EXACT:
> +                       if (!strncmp(buf, "tmpfs", strlen("tmpfs")))
> +                               *outcome = OUTCOME_TMPFS_MOUNT;
> +                       else
> +                               *outcome = OUTCOME_NON_TMPFS_MOUNT;
> +                       break;
> +
> +               case MATCH_PARENT:
> +                       /* This mount obscures any previous ones. */
> +                       *outcome = OUTCOME_NOT_A_MOUNT_POINT;
> +                       break;
> +               }
> +       }
> +
> +       return next(fd, buf, bufsize, '\n');
>  }
>   /* which_tmpdir is called only during early boot */
> @@ -106,8 +217,12 @@ static int checked_tmpdir = 0;
>   */
>  static void which_tmpdir(void)
>  {
> -       int fd, found;
> -       char buf[128] = { '\0' };
> +       int fd;
> +       int found;
> +       int outcome;
> +       char *path;
> +       char *buf;
> +       size_t bufsize;
>         if (checked_tmpdir)
>                 return;
> @@ -116,49 +231,66 @@ static void which_tmpdir(void)
>         printf("Checking for tmpfs mount on /dev/shm...");
>  +      path = realpath("/dev/shm", NULL);
> +       if (!path) {
> +               printf("failed to check real path, errno = %d\n", errno);
> +               return;
> +       }
> +       printf("%s...", path);
> +
> +       /*
> +        * The buffer needs to be able to fit the full octal-escaped path, a
> +        * space, and a trailing null in order to successfully decode it.
> +        */
> +       bufsize = octal_encoded_length(path, " \t\n\\") + 2;
> +
> +       if (bufsize < 128)
> +               bufsize = 128;
> +
> +       buf = malloc(bufsize);
> +       if (!buf) {
> +               printf("malloc failed, errno = %d\n", errno);
> +               goto out;
> +       }
> +       buf[0] = '\0';
> +
>         fd = open("/proc/mounts", O_RDONLY);
>         if (fd < 0) {
>                 printf("failed to open /proc/mounts, errno = %d\n", errno);
> -               return;
> +               goto out1;
>         }
>  +      outcome = OUTCOME_NOT_A_MOUNT_POINT;
>         while (1) {
> -               found = next(fd, buf, ARRAY_SIZE(buf), ' ');
> -               if (found != 1)
> -                       break;
> -
> -               if (!strncmp(buf, "/dev/shm", strlen("/dev/shm")))
> -                       goto found;
> -
> -               found = next(fd, buf, ARRAY_SIZE(buf), '\n');
> +               found = read_mount(fd, buf, bufsize, path, &outcome);
>                 if (found != 1)
>                         break;
>         }
>  -err:
> -       if (found == 0)
> -               printf("nothing mounted on /dev/shm\n");
> -       else if (found < 0)
> +       if (found < 0) {
>                 printf("read returned errno %d\n", -found);
> +       } else {
> +               switch (outcome) {
> +               case OUTCOME_TMPFS_MOUNT:
> +                       printf("OK\n");
> +                       default_tmpdir = "/dev/shm";
> +                       break;
>  -out:
> -       close(fd);
> -
> -       return;
> -
> -found:
> -       found = next(fd, buf, ARRAY_SIZE(buf), ' ');
> -       if (found != 1)
> -               goto err;
> +               case OUTCOME_NON_TMPFS_MOUNT:
> +                       printf("not tmpfs\n");
> +                       break;
>  -      if (strncmp(buf, "tmpfs", strlen("tmpfs"))) {
> -               printf("not tmpfs\n");
> -               goto out;
> +               default:
> +                       printf("nothing mounted on /dev/shm\n");
> +                       break;
> +               }
>         }
>  -      printf("OK\n");
> -       default_tmpdir = "/dev/shm";
> -       goto out;
> +       close(fd);
> +out1:
> +       free(buf);
> +out:
> +       free(path);
>  }
>   static int __init make_tempfile(const char *template, char **out_tempname,
>

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2013-06-19 19:59 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-06-19 19:30 [PATCH] uml: Fix which_tmpdir failure when /dev/shm is a symlink, and in other edge cases Tristan Schmelcher
2013-06-19 19:59 ` Tristan Schmelcher

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.