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 /arch | |
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>
Diffstat (limited to 'arch')
-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 | return; |
@@ -116,49 +247,66 @@ static void which_tmpdir(void) | |||
116 | 247 | ||
117 | printf("Checking for tmpfs mount on /dev/shm..."); | 248 | printf("Checking for tmpfs mount on /dev/shm..."); |
118 | 249 | ||
250 | path = realpath("/dev/shm", NULL); | ||
251 | if (!path) { | ||
252 | printf("failed to check real path, errno = %d\n", errno); | ||
253 | return; | ||
254 | } | ||
255 | printf("%s...", path); | ||
256 | |||
257 | /* | ||
258 | * The buffer needs to be able to fit the full octal-escaped path, a | ||
259 | * space, and a trailing null in order to successfully decode it. | ||
260 | */ | ||
261 | bufsize = octal_encoded_length(path, " \t\n\\") + 2; | ||
262 | |||
263 | if (bufsize < 128) | ||
264 | bufsize = 128; | ||
265 | |||
266 | buf = malloc(bufsize); | ||
267 | if (!buf) { | ||
268 | printf("malloc failed, errno = %d\n", errno); | ||
269 | goto out; | ||
270 | } | ||
271 | buf[0] = '\0'; | ||
272 | |||
119 | fd = open("/proc/mounts", O_RDONLY); | 273 | fd = open("/proc/mounts", O_RDONLY); |
120 | if (fd < 0) { | 274 | if (fd < 0) { |
121 | printf("failed to open /proc/mounts, errno = %d\n", errno); | 275 | printf("failed to open /proc/mounts, errno = %d\n", errno); |
122 | return; | 276 | goto out1; |
123 | } | 277 | } |
124 | 278 | ||
279 | outcome = OUTCOME_NOTHING_MOUNTED; | ||
125 | while (1) { | 280 | while (1) { |
126 | found = next(fd, buf, ARRAY_SIZE(buf), ' '); | 281 | found = read_mount(fd, buf, bufsize, path, &outcome); |
127 | if (found != 1) | ||
128 | break; | ||
129 | |||
130 | if (!strncmp(buf, "/dev/shm", strlen("/dev/shm"))) | ||
131 | goto found; | ||
132 | |||
133 | found = next(fd, buf, ARRAY_SIZE(buf), '\n'); | ||
134 | if (found != 1) | 282 | if (found != 1) |
135 | break; | 283 | break; |
136 | } | 284 | } |
137 | 285 | ||
138 | err: | 286 | if (found < 0) { |
139 | if (found == 0) | ||
140 | printf("nothing mounted on /dev/shm\n"); | ||
141 | else if (found < 0) | ||
142 | printf("read returned errno %d\n", -found); | 287 | printf("read returned errno %d\n", -found); |
288 | } else { | ||
289 | switch (outcome) { | ||
290 | case OUTCOME_TMPFS_MOUNT: | ||
291 | printf("OK\n"); | ||
292 | default_tmpdir = "/dev/shm"; | ||
293 | break; | ||
143 | 294 | ||
144 | out: | 295 | case OUTCOME_NON_TMPFS_MOUNT: |
145 | close(fd); | 296 | printf("not tmpfs\n"); |
146 | 297 | break; | |
147 | return; | ||
148 | |||
149 | found: | ||
150 | found = next(fd, buf, ARRAY_SIZE(buf), ' '); | ||
151 | if (found != 1) | ||
152 | goto err; | ||
153 | 298 | ||
154 | if (strncmp(buf, "tmpfs", strlen("tmpfs"))) { | 299 | default: |
155 | printf("not tmpfs\n"); | 300 | printf("nothing mounted on /dev/shm\n"); |
156 | goto out; | 301 | break; |
302 | } | ||
157 | } | 303 | } |
158 | 304 | ||
159 | printf("OK\n"); | 305 | close(fd); |
160 | default_tmpdir = "/dev/shm"; | 306 | out1: |
161 | goto out; | 307 | free(buf); |
308 | out: | ||
309 | free(path); | ||
162 | } | 310 | } |
163 | 311 | ||
164 | static int __init make_tempfile(const char *template, char **out_tempname, | 312 | static int __init make_tempfile(const char *template, char **out_tempname, |