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