|
@@ -52,6 +52,25 @@ static void __init find_tempdir(void)
|
|
|
strcat(tempdir, "/");
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * 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
|
|
@@ -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,129 @@ static int next(int fd, char *buf, size_t size, char c)
|
|
|
buf[n] = '\0';
|
|
|
}
|
|
|
|
|
|
- ptr++;
|
|
|
- len = strlen(ptr);
|
|
|
- memmove(buf, ptr, len + 1);
|
|
|
+ return pop(fd, buf, size, ptr - buf + 1);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * 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. The output is always null-terminated. "len" gets the
|
|
|
+ * length of the output, excluding the trailing null. Returns 0 if a full path
|
|
|
+ * was successfully decoded, otherwise an error.
|
|
|
+ */
|
|
|
+static int decode_path(const char *in, char *out, size_t *len)
|
|
|
+{
|
|
|
+ char *first = out;
|
|
|
+ int c;
|
|
|
+ int i;
|
|
|
+ int ret = -EINVAL;
|
|
|
+ while (1) {
|
|
|
+ switch (*in) {
|
|
|
+ case '\0':
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ case ' ':
|
|
|
+ ret = 0;
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ case '\\':
|
|
|
+ in++;
|
|
|
+ c = 0;
|
|
|
+ for (i = 0; i < 3; i++) {
|
|
|
+ if (*in < '0' || *in > '7')
|
|
|
+ goto out;
|
|
|
+ c = (c << 3) | (*in++ - '0');
|
|
|
+ }
|
|
|
+ *(unsigned char *)out++ = (unsigned char) c;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ *out++ = *in++;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+out:
|
|
|
+ *out = '\0';
|
|
|
+ *len = out - first;
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Computes the length of s when encoded with three-digit octal escape sequences
|
|
|
+ * for the characters in chars.
|
|
|
+ */
|
|
|
+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_NOTHING_MOUNTED,
|
|
|
+ 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;
|
|
|
+ char *space;
|
|
|
+ size_t len;
|
|
|
+
|
|
|
+ enum {
|
|
|
+ MATCH_NONE,
|
|
|
+ MATCH_EXACT,
|
|
|
+ MATCH_PARENT,
|
|
|
+ };
|
|
|
+
|
|
|
+ found = next(fd, buf, bufsize, ' ');
|
|
|
+ if (found != 1)
|
|
|
+ return found;
|
|
|
|
|
|
/*
|
|
|
- * Refill the buffer so that if there's a partial string that we care
|
|
|
- * about, it will be completed, and we can recognize it.
|
|
|
+ * If there's no following space in the buffer, then this path is
|
|
|
+ * truncated, so it can't be the one we're looking for.
|
|
|
*/
|
|
|
- n = read(fd, &buf[len], size - len - 1);
|
|
|
- if (n < 0)
|
|
|
- return -errno;
|
|
|
+ space = strchr(buf, ' ');
|
|
|
+ if (space) {
|
|
|
+ match = MATCH_NONE;
|
|
|
+ if (!decode_path(buf, buf, &len)) {
|
|
|
+ if (!strcmp(buf, path))
|
|
|
+ match = MATCH_EXACT;
|
|
|
+ else if (!strncmp(buf, path, len)
|
|
|
+ && (path[len] == '/' || !strcmp(buf, "/")))
|
|
|
+ match = MATCH_PARENT;
|
|
|
+ }
|
|
|
+
|
|
|
+ found = pop(fd, buf, bufsize, space - buf + 1);
|
|
|
+ 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;
|
|
|
|
|
|
- buf[len + n] = '\0';
|
|
|
- return 1;
|
|
|
+ case MATCH_PARENT:
|
|
|
+ /* This mount obscures any previous ones. */
|
|
|
+ *outcome = OUTCOME_NOTHING_MOUNTED;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return next(fd, buf, bufsize, '\n');
|
|
|
}
|
|
|
|
|
|
/* which_tmpdir is called only during early boot */
|
|
@@ -106,8 +233,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 +247,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_NOTHING_MOUNTED;
|
|
|
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,
|