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 #ifdef __linux__ 7 #define _GNU_SOURCE 1 8 #endif 9 #include <sys/types.h> 10 #include <sys/param.h> 11 #include <dirent.h> 12 #include <unistd.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <errno.h> 16 #include <stdio.h> 17 #include "qemu-common.h" 18 19 struct pathelem 20 { 21 /* Name of this, eg. lib */ 22 char *name; 23 /* Full path name, eg. /usr/gnemul/x86-linux/lib. */ 24 char *pathname; 25 struct pathelem *parent; 26 /* Children */ 27 unsigned int num_entries; 28 struct pathelem *entries[0]; 29 }; 30 31 static struct pathelem *base; 32 33 /* First N chars of S1 match S2, and S2 is N chars long. */ 34 static int strneq(const char *s1, unsigned int n, const char *s2) 35 { 36 unsigned int i; 37 38 for (i = 0; i < n; i++) 39 if (s1[i] != s2[i]) 40 return 0; 41 return s2[i] == 0; 42 } 43 44 static struct pathelem *add_entry(struct pathelem *root, const char *name, 45 unsigned type); 46 47 static struct pathelem *new_entry(const char *root, 48 struct pathelem *parent, 49 const char *name) 50 { 51 struct pathelem *new = malloc(sizeof(*new)); 52 new->name = strdup(name); 53 new->pathname = g_strdup_printf("%s/%s", root, name); 54 new->num_entries = 0; 55 return new; 56 } 57 58 #define streq(a,b) (strcmp((a), (b)) == 0) 59 60 /* Not all systems provide this feature */ 61 #if defined(DT_DIR) && defined(DT_UNKNOWN) && defined(DT_LNK) 62 # define dirent_type(dirent) ((dirent)->d_type) 63 # define is_dir_maybe(type) \ 64 ((type) == DT_DIR || (type) == DT_UNKNOWN || (type) == DT_LNK) 65 #else 66 # define dirent_type(dirent) (1) 67 # define is_dir_maybe(type) (type) 68 #endif 69 70 static struct pathelem *add_dir_maybe(struct pathelem *path) 71 { 72 DIR *dir; 73 74 if ((dir = opendir(path->pathname)) != NULL) { 75 struct dirent *dirent; 76 77 while ((dirent = readdir(dir)) != NULL) { 78 if (!streq(dirent->d_name,".") && !streq(dirent->d_name,"..")){ 79 path = add_entry(path, dirent->d_name, dirent_type(dirent)); 80 } 81 } 82 closedir(dir); 83 } 84 return path; 85 } 86 87 static struct pathelem *add_entry(struct pathelem *root, const char *name, 88 unsigned type) 89 { 90 struct pathelem **e; 91 92 root->num_entries++; 93 94 root = realloc(root, sizeof(*root) 95 + sizeof(root->entries[0])*root->num_entries); 96 e = &root->entries[root->num_entries-1]; 97 98 *e = new_entry(root->pathname, root, name); 99 if (is_dir_maybe(type)) { 100 *e = add_dir_maybe(*e); 101 } 102 103 return root; 104 } 105 106 /* This needs to be done after tree is stabilized (ie. no more reallocs!). */ 107 static void set_parents(struct pathelem *child, struct pathelem *parent) 108 { 109 unsigned int i; 110 111 child->parent = parent; 112 for (i = 0; i < child->num_entries; i++) 113 set_parents(child->entries[i], child); 114 } 115 116 /* FIXME: Doesn't handle DIR/.. where DIR is not in emulated dir. */ 117 static const char * 118 follow_path(const struct pathelem *cursor, const char *name) 119 { 120 unsigned int i, namelen; 121 122 name += strspn(name, "/"); 123 namelen = strcspn(name, "/"); 124 125 if (namelen == 0) 126 return cursor->pathname; 127 128 if (strneq(name, namelen, "..")) 129 return follow_path(cursor->parent, name + namelen); 130 131 if (strneq(name, namelen, ".")) 132 return follow_path(cursor, name + namelen); 133 134 for (i = 0; i < cursor->num_entries; i++) 135 if (strneq(name, namelen, cursor->entries[i]->name)) 136 return follow_path(cursor->entries[i], name + namelen); 137 138 /* Not found */ 139 return NULL; 140 } 141 142 void init_paths(const char *prefix) 143 { 144 char pref_buf[PATH_MAX]; 145 146 if (prefix[0] == '\0' || 147 !strcmp(prefix, "/")) 148 return; 149 150 if (prefix[0] != '/') { 151 char *cwd = getcwd(NULL, 0); 152 size_t pref_buf_len = sizeof(pref_buf); 153 154 if (!cwd) 155 abort(); 156 pstrcpy(pref_buf, sizeof(pref_buf), cwd); 157 pstrcat(pref_buf, pref_buf_len, "/"); 158 pstrcat(pref_buf, pref_buf_len, prefix); 159 free(cwd); 160 } else 161 pstrcpy(pref_buf, sizeof(pref_buf), prefix + 1); 162 163 base = new_entry("", NULL, pref_buf); 164 base = add_dir_maybe(base); 165 if (base->num_entries == 0) { 166 free (base); 167 base = NULL; 168 } else { 169 set_parents(base, base); 170 } 171 } 172 173 /* Look for path in emulation dir, otherwise return name. */ 174 const char *path(const char *name) 175 { 176 /* Only do absolute paths: quick and dirty, but should mostly be OK. 177 Could do relative by tracking cwd. */ 178 if (!base || !name || name[0] != '/') 179 return name; 180 181 return follow_path(base, name) ?: name; 182 } 183