packing update
[qemu] / linux-user / path.c
1 /* Code to mangle pathnames into those matching a given prefix.
2    eg. open("/lib/foo.so") => open("/usr/gnemul/i386-linux/lib/foo.so");
3
4    The assumption is that this area does not change.
5 */
6 #include <sys/types.h>
7 #include <assert.h>
8 #include <dirent.h>
9 #include <unistd.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <errno.h>
13 #include <stdio.h>
14 #include "qemu.h"
15
16 struct pathelem
17 {
18     /* Name of this, eg. lib */
19     char *name;
20     /* Full path name, eg. /usr/gnemul/x86-linux/lib. */
21     char *pathname;
22     struct pathelem *parent;
23     /* Children */
24     unsigned int num_entries;
25     struct pathelem *entries[0];
26 };
27
28 static struct pathelem *base;
29
30 /* First N chars of S1 match S2, and S2 is N chars long. */
31 static int strneq(const char *s1, unsigned int n, const char *s2)
32 {
33     unsigned int i;
34
35     for (i = 0; i < n; i++)
36         if (s1[i] != s2[i])
37             return 0;
38     return s2[i] == 0;
39 }
40
41 static struct pathelem *add_entry(struct pathelem *root, const char *name);
42
43 static struct pathelem *new_entry(const char *root,
44                                   struct pathelem *parent,
45                                   const char *name)
46 {
47     struct pathelem *new = malloc(sizeof(*new));
48     new->name = strdup(name);
49     asprintf(&new->pathname, "%s/%s", root, name);
50     new->num_entries = 0;
51     return new;
52 }
53
54 #define streq(a,b) (strcmp((a), (b)) == 0)
55
56 /*
57  * Checks whether directory entry (dent) is valid.  This
58  * means that symlinks pointing to '.' and '..' should
59  * be skipped by main recursion code.  Returns 1 when
60  * entry is valid.
61  */
62 static int
63 is_dentry_valid(const char *path, const struct dirent *dent)
64 {
65     char fullpath[PATH_MAX];
66     char linkbuf[PATH_MAX];
67     ssize_t len;
68
69     assert(path != NULL);
70     assert(dent != NULL);
71
72     if (dent->d_type != DT_LNK)
73         return (1);
74
75     (void) snprintf(fullpath, sizeof (fullpath), "%s/%s",
76        path, dent->d_name);
77
78     if ((len = readlink(fullpath, linkbuf, sizeof (linkbuf) - 1)) != -1) {
79         linkbuf[len] = '\0';
80         if (streq(linkbuf, ".") || streq(linkbuf, ".."))
81             return (0);
82     }
83
84     return (1);
85 }
86
87 /* TODO: add recursion count check */
88 static struct pathelem *add_dir_maybe(struct pathelem *path)
89 {
90     DIR *dir;
91
92     if ((dir = opendir(path->pathname)) != NULL) {
93         struct dirent *dirent;
94
95         while ((dirent = readdir(dir)) != NULL) {
96             if (!streq(dirent->d_name,".") && !streq(dirent->d_name,"..")){
97                 if (is_dentry_valid(path->pathname, dirent)) {
98                     path = add_entry(path, dirent->d_name);
99                 }
100             }
101         }
102         closedir(dir);
103     }
104     return path;
105 }
106
107 static struct pathelem *add_entry(struct pathelem *root, const char *name)
108 {
109     root->num_entries++;
110
111     root = realloc(root, sizeof(*root)
112                    + sizeof(root->entries[0])*root->num_entries);
113
114     root->entries[root->num_entries-1] = new_entry(root->pathname, root, name);
115     root->entries[root->num_entries-1]
116         = add_dir_maybe(root->entries[root->num_entries-1]);
117     return root;
118 }
119
120 /* This needs to be done after tree is stabilized (ie. no more reallocs!). */
121 static void set_parents(struct pathelem *child, struct pathelem *parent)
122 {
123     unsigned int i;
124
125     child->parent = parent;
126     for (i = 0; i < child->num_entries; i++)
127         set_parents(child->entries[i], child);
128 }
129
130 /* FIXME: Doesn't handle DIR/.. where DIR is not in emulated dir. */
131 static const char *
132 follow_path(const struct pathelem *cursor, const char *name)
133 {
134     unsigned int i, namelen;
135
136     name += strspn(name, "/");
137     namelen = strcspn(name, "/");
138
139     if (namelen == 0)
140         return cursor->pathname;
141
142     if (strneq(name, namelen, ".."))
143         return follow_path(cursor->parent, name + namelen);
144
145     if (strneq(name, namelen, "."))
146         return follow_path(cursor, name + namelen);
147
148     for (i = 0; i < cursor->num_entries; i++)
149         if (strneq(name, namelen, cursor->entries[i]->name))
150             return follow_path(cursor->entries[i], name + namelen);
151
152     /* Not found */
153     return NULL;
154 }
155
156 void init_paths(const char *prefix)
157 {
158     char pref_buf[PATH_MAX];
159
160     if (prefix[0] == '\0' ||
161         !strcmp(prefix, "/"))
162         return;
163
164     if (prefix[0] != '/') {
165         char *cwd = get_current_dir_name();
166         if (!cwd)
167             abort();
168         strcpy(pref_buf, cwd);
169         strcat(pref_buf, "/");
170         strcat(pref_buf, prefix);
171         free(cwd);
172     } else
173         strcpy(pref_buf,prefix + 1);
174
175     base = new_entry("", NULL, pref_buf);
176     base = add_dir_maybe(base);
177     if (base->num_entries == 0) {
178         free (base);
179         base = NULL;
180     } else {
181         set_parents(base, base);
182     }
183 }
184
185 /* Look for path in emulation dir, otherwise return name. */
186 const char *path(const char *name)
187 {
188     /* Only do absolute paths: quick and dirty, but should mostly be OK.
189        Could do relative by tracking cwd. */
190     if (!base || !name || name[0] != '/')
191         return name;
192
193     return follow_path(base, name) ?: name;
194 }