diff options
| author | Tristan Schmelcher <tschmelcher@google.com> | 2013-07-08 16:19:49 -0400 |
|---|---|---|
| committer | Richard Weinberger <richard@nod.at> | 2013-07-19 04:45:18 -0400 |
| commit | 7473534130c3156d199512c99ff1b796233e8547 (patch) | |
| tree | e92d36be92de59abe737f49e8a23bcb7e1bcf12b | |
| parent | 0974a9cadc7886f7baaa458bb0c89f5c5f9d458e (diff) | |
uml: Fix which_tmpdir failure when /dev/shm is a symlink, and in other edge cases
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>
Signed-off-by: Richard Weinberger <richard@nod.at>
| -rw-r--r-- | arch/um/os-Linux/mem.c | 230 |
1 files changed, 189 insertions, 41 deletions
diff --git a/arch/um/os-Linux/mem.c b/arch/um/os-Linux/mem.c index ba4398056fe9..3c4af77e51a2 100644 --- a/arch/um/os-Linux/mem.c +++ b/arch/um/os-Linux/mem.c | |||
| @@ -53,6 +53,25 @@ static void __init find_tempdir(void) | |||
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | /* | 55 | /* |
| 56 | * Remove bytes from the front of the buffer and refill it so that if there's a | ||
| 57 | * partial string that we care about, it will be completed, and we can recognize | ||
| 58 | * it. | ||
| 59 | */ | ||
| 60 | static int pop(int fd, char *buf, size_t size, size_t npop) | ||
| 61 | { | ||
| 62 | ssize_t n; | ||
| 63 | size_t len = strlen(&buf[npop]); | ||
| 64 | |||
| 65 | memmove(buf, &buf[npop], len + 1); | ||
| 66 | n = read(fd, &buf[len], size - len - 1); | ||
| 67 | if (n < 0) | ||
| 68 | return -errno; | ||
| 69 | |||
| 70 | buf[len + n] = '\0'; | ||
| 71 | return 1; | ||
| 72 | } | ||
| 73 | |||
| 74 | /* | ||
| 56 | * This will return 1, with the first character in buf being the | 75 | * This will return 1, with the first character in buf being the |
| 57 | * character following the next instance of c in the file. This will | 76 | * character following the next instance of c in the file. This will |
| 58 | * read the file as needed. If there's an error, -errno is returned; | 77 | * read the file as needed. If there's an error, -errno is returned; |
| @@ -61,7 +80,6 @@ static void __init find_tempdir(void) | |||
| 61 | static int next(int fd, char *buf, size_t size, char c) | 80 | static int next(int fd, char *buf, size_t size, char c) |
| 62 | { | 81 | { |
| 63 | ssize_t n; | 82 | ssize_t n; |
| 64 | size_t len; | ||
| 65 | char *ptr; | 83 | char *ptr; |
| 66 | 84 | ||
| 67 | while ((ptr = strchr(buf, c)) == NULL) { | 85 | while ((ptr = strchr(buf, c)) == NULL) { |
| @@ -74,20 +92,129 @@ static int next(int fd, char *buf, size_t size, char c) | |||
| 74 | buf[n] = '\0'; | 92 | buf[n] = '\0'; |
| 75 | } | 93 | } |
| 76 | 94 | ||
| 77 | ptr++; | 95 | return pop(fd, buf, size, ptr - buf + 1); |
| 78 | len = strlen(ptr); | 96 | } |
| 79 | memmove(buf, ptr, len + 1); | 97 | |
| 98 | /* | ||
| 99 | * Decode an octal-escaped and space-terminated path of the form used by | ||
| 100 | * /proc/mounts. May be used to decode a path in-place. "out" must be at least | ||
| 101 | * as large as the input. The output is always null-terminated. "len" gets the | ||
| 102 | * length of the output, excluding the trailing null. Returns 0 if a full path | ||
| 103 | * was successfully decoded, otherwise an error. | ||
| 104 | */ | ||
| 105 | static int decode_path(const char *in, char *out, size_t *len) | ||
| 106 | { | ||
| 107 | char *first = out; | ||
| 108 | int c; | ||
| 109 | int i; | ||
| 110 | int ret = -EINVAL; | ||
| 111 | while (1) { | ||
| 112 | switch (*in) { | ||
| 113 | case '\0': | ||
| 114 | goto out; | ||
| 115 | |||
| 116 | case ' ': | ||
| 117 | ret = 0; | ||
| 118 | goto out; | ||
| 119 | |||
| 120 | case '\\': | ||
| 121 | in++; | ||
| 122 | c = 0; | ||
| 123 | for (i = 0; i < 3; i++) { | ||
| 124 | if (*in < '0' || *in > '7') | ||
| 125 | goto out; | ||
| 126 | c = (c << 3) | (*in++ - '0'); | ||
| 127 | } | ||
| 128 | *(unsigned char *)out++ = (unsigned char) c; | ||
| 129 | break; | ||
| 130 | |||
| 131 | default: | ||
| 132 | *out++ = *in++; | ||
| 133 | break; | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | out: | ||
| 138 | *out = '\0'; | ||
| 139 | *len = out - first; | ||
| 140 | return ret; | ||
| 141 | } | ||
| 142 | |||
| 143 | /* | ||
| 144 | * Computes the length of s when encoded with three-digit octal escape sequences | ||
| 145 | * for the characters in chars. | ||
| 146 | */ | ||
| 147 | static size_t octal_encoded_length(const char *s, const char *chars) | ||
| 148 | { | ||
| 149 | size_t len = strlen(s); | ||
| 150 | while ((s = strpbrk(s, chars)) != NULL) { | ||
| 151 | len += 3; | ||
| 152 | s++; | ||
| 153 | } | ||
| 154 | |||
| 155 | return len; | ||
| 156 | } | ||
| 157 | |||
| 158 | enum { | ||
| 159 | OUTCOME_NOTHING_MOUNTED, | ||
| 160 | OUTCOME_TMPFS_MOUNT, | ||
| 161 | OUTCOME_NON_TMPFS_MOUNT, | ||
| 162 | }; | ||
| 163 | |||
| 164 | /* Read a line of /proc/mounts data looking for a tmpfs mount at "path". */ | ||
| 165 | static int read_mount(int fd, char *buf, size_t bufsize, const char *path, | ||
| 166 | int *outcome) | ||
| 167 | { | ||
| 168 | int found; | ||
| 169 | int match; | ||
| 170 | char *space; | ||
| 171 | size_t len; | ||
| 172 | |||
| 173 | enum { | ||
| 174 | MATCH_NONE, | ||
| 175 | MATCH_EXACT, | ||
| 176 | MATCH_PARENT, | ||
| 177 | }; | ||
| 178 | |||
| 179 | found = next(fd, buf, bufsize, ' '); | ||
| 180 | if (found != 1) | ||
| 181 | return found; | ||
| 80 | 182 | ||
| 81 | /* | 183 | /* |
| 82 | * Refill the buffer so that if there's a partial string that we care | 184 | * If there's no following space in the buffer, then this path is |
| 83 | * about, it will be completed, and we can recognize it. | 185 | * truncated, so it can't be the one we're looking for. |
| 84 | */ | 186 | */ |
| 85 | n = read(fd, &buf[len], size - len - 1); | 187 | space = strchr(buf, ' '); |
| 86 | if (n < 0) | 188 | if (space) { |
| 87 | return -errno; | 189 | match = MATCH_NONE; |
| 190 | if (!decode_path(buf, buf, &len)) { | ||
| 191 | if (!strcmp(buf, path)) | ||
| 192 | match = MATCH_EXACT; | ||
| 193 | else if (!strncmp(buf, path, len) | ||
| 194 | && (path[len] == '/' || !strcmp(buf, "/"))) | ||
| 195 | match = MATCH_PARENT; | ||
| 196 | } | ||
| 197 | |||
| 198 | found = pop(fd, buf, bufsize, space - buf + 1); | ||
| 199 | if (found != 1) | ||
| 200 | return found; | ||
| 201 | |||
| 202 | switch (match) { | ||
| 203 | case MATCH_EXACT: | ||
| 204 | if (!strncmp(buf, "tmpfs", strlen("tmpfs"))) | ||
| 205 | *outcome = OUTCOME_TMPFS_MOUNT; | ||
| 206 | else | ||
| 207 | *outcome = OUTCOME_NON_TMPFS_MOUNT; | ||
| 208 | break; | ||
| 88 | 209 | ||
| 89 | buf[len + n] = '\0'; | 210 | case MATCH_PARENT: |
| 90 | return 1; | 211 | /* This mount obscures any previous ones. */ |
| 212 | *outcome = OUTCOME_NOTHING_MOUNTED; | ||
| 213 | break; | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | return next(fd, buf, bufsize, '\n'); | ||
| 91 | } | 218 | } |
| 92 | 219 | ||
| 93 | /* which_tmpdir is called only during early boot */ | 220 | /* which_tmpdir is called only during early boot */ |
| @@ -106,8 +233,12 @@ static int checked_tmpdir = 0; | |||
| 106 | */ | 233 | */ |
| 107 | static void which_tmpdir(void) | 234 | static void which_tmpdir(void) |
| 108 | { | 235 | { |
| 109 | int fd, found; | 236 | int fd; |
| 110 | char buf[128] = { '\0' }; | 237 | int found; |
| 238 | int outcome; | ||
| 239 | char *path; | ||
| 240 | char *buf; | ||
| 241 | size_t bufsize; | ||
| 111 | 242 | ||
| 112 | if (checked_tmpdir) | 243 | if (checked_tmpdir) |
| 113 | return; | 244 | |
