* [Qemu-devel] [PATCH] Fix path mangling in linux-user/path.c
@ 2007-04-22 19:26 Lauri Leukkunen
0 siblings, 0 replies; only message in thread
From: Lauri Leukkunen @ 2007-04-22 19:26 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 250 bytes --]
This patch improves performance at user-mode emulation startup and fixes a nasty
looping bug caused by a symlink such as /usr/bin/X11 -> ../bin
I've tested this using current CVS version on debian/testing on amd64
with arm user-mode target.
/lauri
[-- Attachment #2: qemu-linux-user-path.diff --]
[-- Type: text/x-patch, Size: 8344 bytes --]
Index: path.c
===================================================================
RCS file: /sources/qemu/qemu/linux-user/path.c,v
retrieving revision 1.2
diff -u -r1.2 path.c
--- path.c 13 Sep 2004 21:39:32 -0000 1.2
+++ path.c 22 Apr 2007 19:18:42 -0000
@@ -10,138 +10,191 @@
#include <string.h>
#include <errno.h>
#include <stdio.h>
+#include <libgen.h>
#include "qemu.h"
-struct pathelem
-{
- /* Name of this, eg. lib */
- char *name;
- /* Full path name, eg. /usr/gnemul/x86-linux/lib. */
- char *pathname;
- struct pathelem *parent;
- /* Children */
- unsigned int num_entries;
- struct pathelem *entries[0];
-};
-
-static struct pathelem *base;
-
-/* First N chars of S1 match S2, and S2 is N chars long. */
-static int strneq(const char *s1, unsigned int n, const char *s2)
-{
- unsigned int i;
-
- for (i = 0; i < n; i++)
- if (s1[i] != s2[i])
- return 0;
- return s2[i] == 0;
-}
+char *base = NULL;
-static struct pathelem *add_entry(struct pathelem *root, const char *name);
+struct path_entry {
+ struct path_entry *prev;
+ struct path_entry *next;
+ char name[PATH_MAX];
+};
-static struct pathelem *new_entry(const char *root,
- struct pathelem *parent,
- const char *name)
+char *decolonize_path(const char *path)
{
- struct pathelem *new = malloc(sizeof(*new));
- new->name = strdup(name);
- asprintf(&new->pathname, "%s/%s", root, name);
- new->num_entries = 0;
- return new;
-}
-
-#define streq(a,b) (strcmp((a), (b)) == 0)
+ char *cpath, *index, *start;
+ char cwd[PATH_MAX];
+ struct path_entry list;
+ struct path_entry *work;
+ struct path_entry *new;
+ char *buf = NULL;
-static struct pathelem *add_dir_maybe(struct pathelem *path)
-{
- DIR *dir;
+ if (!path) {
+ return NULL;
+ }
- if ((dir = opendir(path->pathname)) != NULL) {
- struct dirent *dirent;
+ buf = malloc((PATH_MAX + 1) * sizeof(char));
+ memset(buf, '\0', PATH_MAX + 1);
- while ((dirent = readdir(dir)) != NULL) {
- if (!streq(dirent->d_name,".") && !streq(dirent->d_name,"..")){
- path = add_entry(path, dirent->d_name);
- }
- }
- closedir(dir);
+ list.next = NULL;
+ list.prev = NULL;
+ work = &list;
+
+ if (path[0] != '/') {
+ /* not an absolute path */
+ memset(cwd, '\0', PATH_MAX);
+ if (getcwd(cwd, PATH_MAX) < 0) {
+ perror("error getting current work dir\n");
+ return NULL;
+ }
+ unsigned int l = (strlen(cwd) + 1 + strlen(path) + 1);
+ cpath = malloc((strlen(cwd) + 1
+ + strlen(path) + 1) * sizeof(char));
+ memset(cpath, '\0', l);
+ strcpy(cpath, cwd);
+ strcat(cpath, "/");
+ strcat(cpath, path);
+ } else {
+ cpath = strdup(path);
}
- return path;
-}
-
-static struct pathelem *add_entry(struct pathelem *root, const char *name)
-{
- root->num_entries++;
- root = realloc(root, sizeof(*root)
- + sizeof(root->entries[0])*root->num_entries);
+ start = cpath + 1; /* ignore leading '/' */
+ while (1) {
+ index = strstr(start, "/");
+ if (!index) {
+ /* add the last item */
+ new = malloc(sizeof(struct path_entry));
+ memset(new->name, '\0', PATH_MAX);
+ new->prev = work;
+ work->next = new;
+ new->next = NULL;
+ strcpy(new->name, start);
+ work = new;
+ break;
+ }
+ *index = '\0';
+ if (index == (start)) {
+ goto proceed; /* skip over empty strings
+ resulting from // */
+ }
+
+ if (strcmp(start, "..") == 0) {
+ /* travel up one */
+ if (!work->prev)
+ goto proceed;
+ work = work->prev;
+ free(work->next);
+ work->next = NULL;
+ } else if (strcmp(start, ".") == 0) {
+ /* ignore */
+ goto proceed;
+ } else {
+ /* add an entry to our path_entry list */
+ new = malloc(sizeof(struct path_entry));
+ memset(new->name, '\0', PATH_MAX);
+ new->prev = work;
+ work->next = new;
+ new->next = NULL;
+ strcpy(new->name, start);
+ work = new;
+ }
+
+ proceed:
+ *index = '/';
+ start = index + 1;
+ }
- root->entries[root->num_entries-1] = new_entry(root->pathname, root, name);
- root->entries[root->num_entries-1]
- = add_dir_maybe(root->entries[root->num_entries-1]);
- return root;
+ work = list.next;
+ while (work) {
+ struct path_entry *tmp;
+ strcat(buf, "/");
+ strcat(buf, work->name);
+ tmp = work;
+ work = work->next;
+ free(tmp);
+ }
+ return buf;
}
-/* This needs to be done after tree is stabalized (ie. no more reallocs!). */
-static void set_parents(struct pathelem *child, struct pathelem *parent)
-{
- unsigned int i;
-
- child->parent = parent;
- for (i = 0; i < child->num_entries; i++)
- set_parents(child->entries[i], child);
-}
void init_paths(const char *prefix)
{
- if (prefix[0] != '/' ||
- prefix[0] == '\0' ||
- !strcmp(prefix, "/"))
- return;
-
- base = new_entry("", NULL, prefix+1);
- base = add_dir_maybe(base);
- if (base->num_entries == 0) {
- free (base);
- base = NULL;
- } else {
- set_parents(base, base);
- }
+ base = strdup(prefix);
}
-/* FIXME: Doesn't handle DIR/.. where DIR is not in emulated dir. */
-static const char *
-follow_path(const struct pathelem *cursor, const char *name)
+
+
+char *adjust_for_leakage(char *path)
{
- unsigned int i, namelen;
+ char tmp[PATH_MAX + 1];
+ char tmp2[PATH_MAX + 1];
+ char *buf;
+ char *bname = NULL, *dname = NULL;
+ int i;
- name += strspn(name, "/");
- namelen = strcspn(name, "/");
+ if (!path)
+ return NULL;
- if (namelen == 0)
- return cursor->pathname;
+ memset(tmp, '\0', PATH_MAX + 1);
+ memset(tmp2, '\0', PATH_MAX + 1);
+ if ((i = readlink(path, tmp, PATH_MAX) < 0)) {
+ /* not a symlink */
+ return strdup(path);
+ }
- if (strneq(name, namelen, ".."))
- return follow_path(cursor->parent, name + namelen);
+ bname = basename(strdup(path));
+ dname = dirname(strdup(path));
- if (strneq(name, namelen, "."))
- return follow_path(cursor, name + namelen);
+ /* check if the symlink refers to itself */
+ if (strcmp(tmp, bname) == 0) {
+ /* symlink refers to itself */
+ return strdup(path);
+ }
- for (i = 0; i < cursor->num_entries; i++)
- if (strneq(name, namelen, cursor->entries[i]->name))
- return follow_path(cursor->entries[i], name + namelen);
+ /* make tmp absolute if it's not */
+ if (tmp[0] != '/') {
+ strcpy(tmp2, dname);
+ strcat(tmp2, "/");
+ strcat(tmp2, tmp);
+ }
- /* Not found */
- return NULL;
+ /* remove "." and ".." entries from tmp, done in-place */
+ buf = decolonize_path(tmp2);
+ strcpy(tmp2, buf);
+
+ free(buf);
+ free(bname);
+ free(dname);
+
+ if (strncmp(tmp2, base, strlen(base)) != 0) {
+ /* tried to leak out, fix it */
+ strcpy(tmp, base);
+ strcpy(tmp, "/");
+ strcpy(tmp, tmp2);
+ return adjust_for_leakage(tmp);
+ } else {
+ return adjust_for_leakage(tmp2);
+ }
}
/* Look for path in emulation dir, otherwise return name. */
const char *path(const char *name)
{
- /* Only do absolute paths: quick and dirty, but should mostly be OK.
- Could do relative by tracking cwd. */
- if (!base || name[0] != '/')
- return name;
+ char *tmp, *ret;
- return follow_path(base, name) ?: name;
+ if (strncmp(name, base, strlen(base)) == 0) {
+ /* name is within emulation dir */
+ ret = adjust_for_leakage(name);
+ } else {
+ /* name is elsewhere, use base + "/" + name */
+ tmp = malloc(sizeof(char) * strlen(base) + strlen(name) + 2);
+ strcpy(tmp, base);
+ if (name[0] != '/')
+ strcat(tmp, "/");
+ strcat(tmp, name);
+ ret = adjust_for_leakage(tmp);
+ free(tmp);
+ }
+ return ret;
}
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2007-04-22 19:32 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-04-22 19:26 [Qemu-devel] [PATCH] Fix path mangling in linux-user/path.c Lauri Leukkunen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).